1use crate::spec::Bootloader;
6use anyhow::{Context, Result};
7use clap::ValueEnum;
8use fn_error_context::context;
9use serde::{Deserialize, Serialize};
10
11#[cfg(feature = "install-to-disk")]
12use super::baseline::BlockSetup;
13
14pub(crate) struct EnvProperties {
17 pub(crate) sys_arch: String,
18}
19
20#[derive(clap::ValueEnum, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
22#[serde(rename_all = "kebab-case")]
23pub(crate) enum Filesystem {
24 Xfs,
25 Ext4,
26 Btrfs,
27}
28
29impl std::fmt::Display for Filesystem {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 self.to_possible_value().unwrap().get_name().fmt(f)
32 }
33}
34
35impl TryFrom<&str> for Filesystem {
36 type Error = anyhow::Error;
37
38 fn try_from(value: &str) -> Result<Self, Self::Error> {
39 match value {
40 "xfs" => Ok(Self::Xfs),
41 "ext4" => Ok(Self::Ext4),
42 "btrfs" => Ok(Self::Btrfs),
43 other => anyhow::bail!("Unknown filesystem: {}", other),
44 }
45 }
46}
47
48impl Filesystem {
49 pub(crate) fn supports_fsverity(&self) -> bool {
50 matches!(self, Self::Ext4 | Self::Btrfs)
51 }
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize, Default)]
57#[serde(deny_unknown_fields)]
58pub(crate) struct InstallConfigurationToplevel {
59 pub(crate) install: Option<InstallConfiguration>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, Default)]
64#[serde(deny_unknown_fields)]
65pub(crate) struct RootFS {
66 #[serde(rename = "type")]
67 pub(crate) fstype: Option<Filesystem>,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize, Default)]
73#[serde(deny_unknown_fields)]
74pub(crate) struct BasicFilesystems {
75 pub(crate) root: Option<RootFS>,
76 }
80
81pub(crate) type OstreeRepoOpts = ostree_ext::repo_options::RepoOptions;
83
84#[derive(Debug, Clone, Serialize, Deserialize, Default)]
86#[serde(rename_all = "kebab-case", deny_unknown_fields)]
87pub(crate) struct Bootupd {
88 pub(crate) skip_boot_uuid: Option<bool>,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, Default)]
96#[serde(rename = "install", rename_all = "kebab-case", deny_unknown_fields)]
97pub(crate) struct InstallConfiguration {
98 pub(crate) root_fs_type: Option<Filesystem>,
100 #[cfg(feature = "install-to-disk")]
102 pub(crate) block: Option<Vec<BlockSetup>>,
103 pub(crate) filesystem: Option<BasicFilesystems>,
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub(crate) kargs: Option<Vec<String>>,
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub(crate) karg_deletes: Option<Vec<String>>,
110 pub(crate) match_architectures: Option<Vec<String>>,
112 pub(crate) ostree: Option<OstreeRepoOpts>,
114 pub(crate) stateroot: Option<String>,
116 pub(crate) root_mount_spec: Option<String>,
119 pub(crate) boot_mount_spec: Option<String>,
121 pub(crate) bootupd: Option<Bootupd>,
123 pub(crate) bootloader: Option<Bootloader>,
125 pub(crate) discoverable_partitions: Option<bool>,
132 pub(crate) enforce_container_sigpolicy: Option<bool>,
135}
136
137fn merge_basic<T>(s: &mut Option<T>, o: Option<T>, _env: &EnvProperties) {
138 if let Some(o) = o {
139 *s = Some(o);
140 }
141}
142
143trait Mergeable {
144 fn merge(&mut self, other: Self, env: &EnvProperties)
145 where
146 Self: Sized;
147}
148
149impl<T> Mergeable for Option<T>
150where
151 T: Mergeable,
152{
153 fn merge(&mut self, other: Self, env: &EnvProperties)
154 where
155 Self: Sized,
156 {
157 if let Some(other) = other {
158 if let Some(s) = self.as_mut() {
159 s.merge(other, env)
160 } else {
161 *self = Some(other);
162 }
163 }
164 }
165}
166
167impl Mergeable for RootFS {
168 fn merge(&mut self, other: Self, env: &EnvProperties) {
170 merge_basic(&mut self.fstype, other.fstype, env)
171 }
172}
173
174impl Mergeable for BasicFilesystems {
175 fn merge(&mut self, other: Self, env: &EnvProperties) {
177 self.root.merge(other.root, env)
178 }
179}
180
181impl Mergeable for OstreeRepoOpts {
182 fn merge(&mut self, other: Self, env: &EnvProperties) {
184 merge_basic(
185 &mut self.bls_append_except_default,
186 other.bls_append_except_default,
187 env,
188 )
189 }
190}
191
192impl Mergeable for Bootupd {
193 fn merge(&mut self, other: Self, env: &EnvProperties) {
195 merge_basic(&mut self.skip_boot_uuid, other.skip_boot_uuid, env)
196 }
197}
198
199impl Mergeable for InstallConfiguration {
200 fn merge(&mut self, other: Self, env: &EnvProperties) {
202 if other
205 .match_architectures
206 .map(|a| a.contains(&env.sys_arch))
207 .unwrap_or(true)
208 {
209 merge_basic(&mut self.root_fs_type, other.root_fs_type, env);
210 #[cfg(feature = "install-to-disk")]
211 merge_basic(&mut self.block, other.block, env);
212 self.filesystem.merge(other.filesystem, env);
213 self.ostree.merge(other.ostree, env);
214 merge_basic(&mut self.stateroot, other.stateroot, env);
215 merge_basic(&mut self.root_mount_spec, other.root_mount_spec, env);
216 merge_basic(&mut self.boot_mount_spec, other.boot_mount_spec, env);
217 self.bootupd.merge(other.bootupd, env);
218 merge_basic(&mut self.bootloader, other.bootloader, env);
219 merge_basic(
220 &mut self.discoverable_partitions,
221 other.discoverable_partitions,
222 env,
223 );
224 merge_basic(
225 &mut self.enforce_container_sigpolicy,
226 other.enforce_container_sigpolicy,
227 env,
228 );
229 if let Some(other_kargs) = other.kargs {
230 self.kargs
231 .get_or_insert_with(Default::default)
232 .extend(other_kargs)
233 }
234 if let Some(other_karg_deletes) = other.karg_deletes {
235 self.karg_deletes
236 .get_or_insert_with(Default::default)
237 .extend(other_karg_deletes)
238 }
239 }
240 }
241}
242
243impl InstallConfiguration {
244 pub(crate) fn canonicalize(&mut self) {
250 if let Some(rootfs_type) = self.filesystem_root().and_then(|f| f.fstype.as_ref()) {
252 self.root_fs_type = Some(*rootfs_type)
253 } else if let Some(rootfs) = self.root_fs_type.as_ref() {
254 let fs = self.filesystem.get_or_insert_with(Default::default);
255 let root = fs.root.get_or_insert_with(Default::default);
256 root.fstype = Some(*rootfs);
257 }
258
259 #[cfg(feature = "install-to-disk")]
260 if self.block.is_none() {
261 self.block = Some(vec![BlockSetup::Direct]);
262 }
263 }
264
265 pub(crate) fn filesystem_root(&self) -> Option<&RootFS> {
267 self.filesystem.as_ref().and_then(|fs| fs.root.as_ref())
268 }
269
270 pub(crate) fn filter_to_external(&mut self) {
272 self.kargs.take();
273 self.karg_deletes.take();
274 }
275
276 #[cfg(feature = "install-to-disk")]
277 pub(crate) fn get_block_setup(&self, default: Option<BlockSetup>) -> Result<BlockSetup> {
278 let valid_block_setups = self.block.as_deref().unwrap_or_default();
279 let default_block = valid_block_setups.iter().next().ok_or_else(|| {
280 anyhow::anyhow!("Empty block storage configuration in install configuration")
281 })?;
282 let block_setup = default.as_ref().unwrap_or(default_block);
283 if !valid_block_setups.contains(block_setup) {
284 anyhow::bail!("Block setup {block_setup:?} is not enabled in installation config");
285 }
286 Ok(*block_setup)
287 }
288}
289
290#[context("Loading configuration")]
291pub(crate) fn load_config() -> Result<Option<InstallConfiguration>> {
293 let env = EnvProperties {
294 sys_arch: std::env::consts::ARCH.to_string(),
295 };
296 const SYSTEMD_CONVENTIONAL_BASES: &[&str] = &["/usr/lib", "/usr/local/lib", "/etc", "/run"];
297 let fragments = liboverdrop::scan(SYSTEMD_CONVENTIONAL_BASES, "bootc/install", &["toml"], true);
298 let mut config: Option<InstallConfiguration> = None;
299 for (_name, path) in fragments {
300 let buf = std::fs::read_to_string(&path)?;
301 let mut unused = std::collections::HashSet::new();
302 let de = toml::Deserializer::parse(&buf).with_context(|| format!("Parsing {path:?}"))?;
303 let mut c: InstallConfigurationToplevel = serde_ignored::deserialize(de, |path| {
304 unused.insert(path.to_string());
305 })
306 .with_context(|| format!("Parsing {path:?}"))?;
307 for key in unused {
308 eprintln!("warning: {path:?}: Unknown key {key}");
309 }
310 if let Some(config) = config.as_mut() {
311 if let Some(install) = c.install {
312 tracing::debug!("Merging install config: {install:?}");
313 config.merge(install, &env);
314 }
315 } else {
316 if let Some(ref mut install) = c.install {
319 if install
320 .match_architectures
321 .as_ref()
322 .map(|a| a.contains(&env.sys_arch))
323 .unwrap_or(true)
324 {
325 config = c.install;
326 }
327 }
328 }
329 }
330 if let Some(config) = config.as_mut() {
331 config.canonicalize();
332 }
333 Ok(config)
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339
340 #[test]
341 fn test_parse_config() {
343 let env = EnvProperties {
344 sys_arch: "x86_64".to_string(),
345 };
346 let c: InstallConfigurationToplevel = toml::from_str(
347 r##"[install]
348root-fs-type = "xfs"
349"##,
350 )
351 .unwrap();
352 let mut install = c.install.unwrap();
353 assert_eq!(install.root_fs_type.unwrap(), Filesystem::Xfs);
354 let other = InstallConfigurationToplevel {
355 install: Some(InstallConfiguration {
356 root_fs_type: Some(Filesystem::Ext4),
357 ..Default::default()
358 }),
359 };
360 install.merge(other.install.unwrap(), &env);
361 assert_eq!(
362 install.root_fs_type.as_ref().copied().unwrap(),
363 Filesystem::Ext4
364 );
365 assert!(install.filesystem_root().is_none());
367 install.canonicalize();
368 assert_eq!(install.root_fs_type.as_ref().unwrap(), &Filesystem::Ext4);
369 assert_eq!(
370 install.filesystem_root().unwrap().fstype.unwrap(),
371 Filesystem::Ext4
372 );
373
374 let c: InstallConfigurationToplevel = toml::from_str(
375 r##"[install]
376root-fs-type = "ext4"
377kargs = ["console=ttyS0", "foo=bar"]
378karg-deletes = ["debug", "bar=baz"]
379"##,
380 )
381 .unwrap();
382 let mut install = c.install.unwrap();
383 assert_eq!(install.root_fs_type.unwrap(), Filesystem::Ext4);
384 let other = InstallConfigurationToplevel {
385 install: Some(InstallConfiguration {
386 kargs: Some(
387 ["console=tty0", "nosmt"]
388 .into_iter()
389 .map(ToOwned::to_owned)
390 .collect(),
391 ),
392 karg_deletes: Some(
393 ["baz", "bar=baz"]
394 .into_iter()
395 .map(ToOwned::to_owned)
396 .collect(),
397 ),
398 ..Default::default()
399 }),
400 };
401 install.merge(other.install.unwrap(), &env);
402 assert_eq!(install.root_fs_type.unwrap(), Filesystem::Ext4);
403 assert_eq!(
404 install.kargs,
405 Some(
406 ["console=ttyS0", "foo=bar", "console=tty0", "nosmt"]
407 .into_iter()
408 .map(ToOwned::to_owned)
409 .collect()
410 )
411 );
412 assert_eq!(
413 install.karg_deletes,
414 Some(
415 ["debug", "bar=baz", "baz", "bar=baz"]
416 .into_iter()
417 .map(ToOwned::to_owned)
418 .collect()
419 )
420 );
421 }
422
423 #[test]
424 fn test_parse_filesystems() {
425 let env = EnvProperties {
426 sys_arch: "x86_64".to_string(),
427 };
428 let c: InstallConfigurationToplevel = toml::from_str(
429 r##"[install.filesystem.root]
430type = "xfs"
431"##,
432 )
433 .unwrap();
434 let mut install = c.install.unwrap();
435 assert_eq!(
436 install.filesystem_root().unwrap().fstype.unwrap(),
437 Filesystem::Xfs
438 );
439 let other = InstallConfigurationToplevel {
440 install: Some(InstallConfiguration {
441 filesystem: Some(BasicFilesystems {
442 root: Some(RootFS {
443 fstype: Some(Filesystem::Ext4),
444 }),
445 }),
446 ..Default::default()
447 }),
448 };
449 install.merge(other.install.unwrap(), &env);
450 assert_eq!(
451 install.filesystem_root().unwrap().fstype.unwrap(),
452 Filesystem::Ext4
453 );
454 }
455
456 #[test]
457 #[cfg(feature = "install-to-disk")]
458 fn test_parse_block() {
459 let env = EnvProperties {
460 sys_arch: "x86_64".to_string(),
461 };
462 let c: InstallConfigurationToplevel = toml::from_str(
463 r##"[install.filesystem.root]
464type = "xfs"
465"##,
466 )
467 .unwrap();
468 let mut install = c.install.unwrap();
469 {
471 let mut install = install.clone();
472 install.canonicalize();
473 assert_eq!(install.get_block_setup(None).unwrap(), BlockSetup::Direct);
474 }
475 let other = InstallConfigurationToplevel {
476 install: Some(InstallConfiguration {
477 block: Some(vec![]),
478 ..Default::default()
479 }),
480 };
481 install.merge(other.install.unwrap(), &env);
482 assert_eq!(install.block.as_ref().unwrap().len(), 0);
484 assert!(install.get_block_setup(None).is_err());
485
486 let c: InstallConfigurationToplevel = toml::from_str(
487 r##"[install]
488block = ["tpm2-luks"]"##,
489 )
490 .unwrap();
491 let mut install = c.install.unwrap();
492 install.canonicalize();
493 assert_eq!(install.block.as_ref().unwrap().len(), 1);
494 assert_eq!(install.get_block_setup(None).unwrap(), BlockSetup::Tpm2Luks);
495
496 assert!(install.get_block_setup(Some(BlockSetup::Direct)).is_err());
498 }
499
500 #[test]
501 fn test_arch() {
503 let env = EnvProperties {
505 sys_arch: "x86_64".to_string(),
506 };
507 let c: InstallConfigurationToplevel = toml::from_str(
508 r##"[install]
509root-fs-type = "xfs"
510"##,
511 )
512 .unwrap();
513 let mut install = c.install.unwrap();
514 let other = InstallConfigurationToplevel {
515 install: Some(InstallConfiguration {
516 kargs: Some(
517 ["console=tty0", "nosmt"]
518 .into_iter()
519 .map(ToOwned::to_owned)
520 .collect(),
521 ),
522 ..Default::default()
523 }),
524 };
525 install.merge(other.install.unwrap(), &env);
526 assert_eq!(
527 install.kargs,
528 Some(
529 ["console=tty0", "nosmt"]
530 .into_iter()
531 .map(ToOwned::to_owned)
532 .collect()
533 )
534 );
535 let env = EnvProperties {
536 sys_arch: "aarch64".to_string(),
537 };
538 let c: InstallConfigurationToplevel = toml::from_str(
539 r##"[install]
540root-fs-type = "xfs"
541"##,
542 )
543 .unwrap();
544 let mut install = c.install.unwrap();
545 let other = InstallConfigurationToplevel {
546 install: Some(InstallConfiguration {
547 kargs: Some(
548 ["console=tty0", "nosmt"]
549 .into_iter()
550 .map(ToOwned::to_owned)
551 .collect(),
552 ),
553 ..Default::default()
554 }),
555 };
556 install.merge(other.install.unwrap(), &env);
557 assert_eq!(
558 install.kargs,
559 Some(
560 ["console=tty0", "nosmt"]
561 .into_iter()
562 .map(ToOwned::to_owned)
563 .collect()
564 )
565 );
566
567 let env = EnvProperties {
569 sys_arch: "aarch64".to_string(),
570 };
571 let c: InstallConfigurationToplevel = toml::from_str(
572 r##"[install]
573root-fs-type = "xfs"
574"##,
575 )
576 .unwrap();
577 let mut install = c.install.unwrap();
578 let other = InstallConfigurationToplevel {
579 install: Some(InstallConfiguration {
580 kargs: Some(
581 ["console=ttyS0", "foo=bar"]
582 .into_iter()
583 .map(ToOwned::to_owned)
584 .collect(),
585 ),
586 match_architectures: Some(["x86_64"].into_iter().map(ToOwned::to_owned).collect()),
587 ..Default::default()
588 }),
589 };
590 install.merge(other.install.unwrap(), &env);
591 assert_eq!(install.kargs, None);
592 let other = InstallConfigurationToplevel {
593 install: Some(InstallConfiguration {
594 kargs: Some(
595 ["console=tty0", "nosmt"]
596 .into_iter()
597 .map(ToOwned::to_owned)
598 .collect(),
599 ),
600 match_architectures: Some(["aarch64"].into_iter().map(ToOwned::to_owned).collect()),
601 ..Default::default()
602 }),
603 };
604 install.merge(other.install.unwrap(), &env);
605 assert_eq!(
606 install.kargs,
607 Some(
608 ["console=tty0", "nosmt"]
609 .into_iter()
610 .map(ToOwned::to_owned)
611 .collect()
612 )
613 );
614
615 let env = EnvProperties {
617 sys_arch: "x86_64".to_string(),
618 };
619 let c: InstallConfigurationToplevel = toml::from_str(
620 r##"[install]
621root-fs-type = "xfs"
622"##,
623 )
624 .unwrap();
625 let mut install = c.install.unwrap();
626 let other = InstallConfigurationToplevel {
627 install: Some(InstallConfiguration {
628 kargs: Some(
629 ["console=tty0", "nosmt"]
630 .into_iter()
631 .map(ToOwned::to_owned)
632 .collect(),
633 ),
634 match_architectures: Some(
635 ["x86_64", "aarch64"]
636 .into_iter()
637 .map(ToOwned::to_owned)
638 .collect(),
639 ),
640 ..Default::default()
641 }),
642 };
643 install.merge(other.install.unwrap(), &env);
644 assert_eq!(
645 install.kargs,
646 Some(
647 ["console=tty0", "nosmt"]
648 .into_iter()
649 .map(ToOwned::to_owned)
650 .collect()
651 )
652 );
653 let env = EnvProperties {
654 sys_arch: "aarch64".to_string(),
655 };
656 let c: InstallConfigurationToplevel = toml::from_str(
657 r##"[install]
658root-fs-type = "xfs"
659"##,
660 )
661 .unwrap();
662 let mut install = c.install.unwrap();
663 let other = InstallConfigurationToplevel {
664 install: Some(InstallConfiguration {
665 kargs: Some(
666 ["console=tty0", "nosmt"]
667 .into_iter()
668 .map(ToOwned::to_owned)
669 .collect(),
670 ),
671 match_architectures: Some(
672 ["x86_64", "aarch64"]
673 .into_iter()
674 .map(ToOwned::to_owned)
675 .collect(),
676 ),
677 ..Default::default()
678 }),
679 };
680 install.merge(other.install.unwrap(), &env);
681 assert_eq!(
682 install.kargs,
683 Some(
684 ["console=tty0", "nosmt"]
685 .into_iter()
686 .map(ToOwned::to_owned)
687 .collect()
688 )
689 );
690 }
691
692 #[test]
693 fn test_parse_ostree() {
694 let env = EnvProperties {
695 sys_arch: "x86_64".to_string(),
696 };
697
698 let parse_cases = [
700 ("console=ttyS0", "console=ttyS0"),
701 ("console=ttyS0,115200n8", "console=ttyS0,115200n8"),
702 ("rd.lvm.lv=vg/root", "rd.lvm.lv=vg/root"),
703 ];
704 for (input, expected) in parse_cases {
705 let toml_str = format!(
706 r#"[install.ostree]
707bls-append-except-default = "{input}"
708"#
709 );
710 let c: InstallConfigurationToplevel = toml::from_str(&toml_str).unwrap();
711 assert_eq!(
712 c.install
713 .unwrap()
714 .ostree
715 .unwrap()
716 .bls_append_except_default
717 .unwrap(),
718 expected
719 );
720 }
721
722 let mut install: InstallConfiguration = toml::from_str(
724 r#"[ostree]
725bls-append-except-default = "console=ttyS0"
726"#,
727 )
728 .unwrap();
729 let other = InstallConfiguration {
730 ostree: Some(OstreeRepoOpts {
731 bls_append_except_default: Some("console=tty0".to_string()),
732 ..Default::default()
733 }),
734 ..Default::default()
735 };
736 install.merge(other, &env);
737 assert_eq!(
738 install.ostree.unwrap().bls_append_except_default.unwrap(),
739 "console=tty0"
740 );
741 }
742
743 #[test]
744 fn test_parse_stateroot() {
745 let c: InstallConfigurationToplevel = toml::from_str(
746 r#"[install]
747stateroot = "custom"
748"#,
749 )
750 .unwrap();
751 assert_eq!(c.install.unwrap().stateroot.unwrap(), "custom");
752 }
753
754 #[test]
755 fn test_merge_stateroot() {
756 let env = EnvProperties {
757 sys_arch: "x86_64".to_string(),
758 };
759 let mut install: InstallConfiguration = toml::from_str(
760 r#"stateroot = "original"
761"#,
762 )
763 .unwrap();
764 let other = InstallConfiguration {
765 stateroot: Some("newroot".to_string()),
766 ..Default::default()
767 };
768 install.merge(other, &env);
769 assert_eq!(install.stateroot.unwrap(), "newroot");
770 }
771
772 #[test]
773 fn test_parse_mount_specs() {
774 let c: InstallConfigurationToplevel = toml::from_str(
775 r#"[install]
776root-mount-spec = "LABEL=rootfs"
777boot-mount-spec = "UUID=abcd-1234"
778"#,
779 )
780 .unwrap();
781 let install = c.install.unwrap();
782 assert_eq!(install.root_mount_spec.unwrap(), "LABEL=rootfs");
783 assert_eq!(install.boot_mount_spec.unwrap(), "UUID=abcd-1234");
784 }
785
786 #[test]
787 fn test_merge_mount_specs() {
788 let env = EnvProperties {
789 sys_arch: "x86_64".to_string(),
790 };
791 let mut install: InstallConfiguration = toml::from_str(
792 r#"root-mount-spec = "UUID=old"
793boot-mount-spec = "UUID=oldboot"
794"#,
795 )
796 .unwrap();
797 let other = InstallConfiguration {
798 root_mount_spec: Some("LABEL=newroot".to_string()),
799 ..Default::default()
800 };
801 install.merge(other, &env);
802 assert_eq!(install.root_mount_spec.as_deref().unwrap(), "LABEL=newroot");
804 assert_eq!(install.boot_mount_spec.as_deref().unwrap(), "UUID=oldboot");
806 }
807
808 #[test]
811 fn test_parse_empty_mount_specs() {
812 let c: InstallConfigurationToplevel = toml::from_str(
813 r#"[install]
814root-mount-spec = ""
815boot-mount-spec = ""
816"#,
817 )
818 .unwrap();
819 let install = c.install.unwrap();
820 assert_eq!(install.root_mount_spec.as_deref().unwrap(), "");
821 assert_eq!(install.boot_mount_spec.as_deref().unwrap(), "");
822 }
823
824 #[test]
825 fn test_parse_bootupd_skip_boot_uuid() {
826 let c: InstallConfigurationToplevel = toml::from_str(
828 r#"[install.bootupd]
829skip-boot-uuid = true
830"#,
831 )
832 .unwrap();
833 assert_eq!(
834 c.install.unwrap().bootupd.unwrap().skip_boot_uuid.unwrap(),
835 true
836 );
837
838 let c: InstallConfigurationToplevel = toml::from_str(
840 r#"[install.bootupd]
841skip-boot-uuid = false
842"#,
843 )
844 .unwrap();
845 assert_eq!(
846 c.install.unwrap().bootupd.unwrap().skip_boot_uuid.unwrap(),
847 false
848 );
849
850 let c: InstallConfigurationToplevel = toml::from_str(
852 r#"[install]
853root-fs-type = "xfs"
854"#,
855 )
856 .unwrap();
857 assert!(c.install.unwrap().bootupd.is_none());
858 }
859
860 #[test]
861 fn test_merge_bootupd_skip_boot_uuid() {
862 let env = EnvProperties {
863 sys_arch: "x86_64".to_string(),
864 };
865 let mut install: InstallConfiguration = toml::from_str(
866 r#"[bootupd]
867skip-boot-uuid = false
868"#,
869 )
870 .unwrap();
871 let other = InstallConfiguration {
872 bootupd: Some(Bootupd {
873 skip_boot_uuid: Some(true),
874 }),
875 ..Default::default()
876 };
877 install.merge(other, &env);
878 assert_eq!(install.bootupd.unwrap().skip_boot_uuid.unwrap(), true);
880 }
881}
882
883#[test]
884fn test_parse_bootloader() {
885 let env = EnvProperties {
886 sys_arch: "x86_64".to_string(),
887 };
888
889 let c: InstallConfigurationToplevel = toml::from_str(
891 r##"[install]
892bootloader = "none"
893"##,
894 )
895 .unwrap();
896 assert_eq!(c.install.unwrap().bootloader, Some(Bootloader::None));
897
898 let c: InstallConfigurationToplevel = toml::from_str(
900 r##"[install]
901bootloader = "grub"
902"##,
903 )
904 .unwrap();
905 assert_eq!(c.install.unwrap().bootloader, Some(Bootloader::Grub));
906
907 let mut install: InstallConfiguration = toml::from_str(
910 r#"bootloader = "systemd"
911"#,
912 )
913 .unwrap();
914
915 let other = InstallConfiguration {
917 bootloader: Some(Bootloader::None),
918 ..Default::default()
919 };
920
921 install.merge(other, &env);
923 assert_eq!(install.bootloader, Some(Bootloader::None));
924}
925
926#[test]
927fn test_parse_discoverable_partitions() {
928 let c: InstallConfigurationToplevel = toml::from_str(
929 r##"[install]
930discoverable-partitions = true
931"##,
932 )
933 .unwrap();
934 assert_eq!(c.install.unwrap().discoverable_partitions, Some(true));
935
936 let c: InstallConfigurationToplevel = toml::from_str(
937 r##"[install]
938discoverable-partitions = false
939"##,
940 )
941 .unwrap();
942 assert_eq!(c.install.unwrap().discoverable_partitions, Some(false));
943
944 let c: InstallConfigurationToplevel = toml::from_str(
945 r##"[install]
946root-fs-type = "xfs"
947"##,
948 )
949 .unwrap();
950 assert_eq!(c.install.unwrap().discoverable_partitions, None);
951}
952
953#[test]
954fn test_parse_enforce_container_sigpolicy() {
955 let env = EnvProperties {
956 sys_arch: "x86_64".to_string(),
957 };
958
959 for (input, expected) in [("true", true), ("false", false)] {
961 let toml_str = format!(
962 r#"[install]
963enforce-container-sigpolicy = {input}
964"#
965 );
966 let c: InstallConfigurationToplevel = toml::from_str(&toml_str).unwrap();
967 assert_eq!(
968 c.install.unwrap().enforce_container_sigpolicy.unwrap(),
969 expected
970 );
971 }
972
973 let c: InstallConfigurationToplevel = toml::from_str(
975 r#"[install]
976root-fs-type = "xfs"
977"#,
978 )
979 .unwrap();
980 assert!(c.install.unwrap().enforce_container_sigpolicy.is_none());
981
982 let mut install: InstallConfiguration = toml::from_str(
984 r#"enforce-container-sigpolicy = false
985"#,
986 )
987 .unwrap();
988 let other = InstallConfiguration {
989 enforce_container_sigpolicy: Some(true),
990 ..Default::default()
991 };
992 install.merge(other, &env);
993 assert_eq!(install.enforce_container_sigpolicy.unwrap(), true);
994}