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 fn test_parse_block() {
458 let env = EnvProperties {
459 sys_arch: "x86_64".to_string(),
460 };
461 let c: InstallConfigurationToplevel = toml::from_str(
462 r##"[install.filesystem.root]
463type = "xfs"
464"##,
465 )
466 .unwrap();
467 let mut install = c.install.unwrap();
468 {
470 let mut install = install.clone();
471 install.canonicalize();
472 assert_eq!(install.get_block_setup(None).unwrap(), BlockSetup::Direct);
473 }
474 let other = InstallConfigurationToplevel {
475 install: Some(InstallConfiguration {
476 block: Some(vec![]),
477 ..Default::default()
478 }),
479 };
480 install.merge(other.install.unwrap(), &env);
481 assert_eq!(install.block.as_ref().unwrap().len(), 0);
483 assert!(install.get_block_setup(None).is_err());
484
485 let c: InstallConfigurationToplevel = toml::from_str(
486 r##"[install]
487block = ["tpm2-luks"]"##,
488 )
489 .unwrap();
490 let mut install = c.install.unwrap();
491 install.canonicalize();
492 assert_eq!(install.block.as_ref().unwrap().len(), 1);
493 assert_eq!(install.get_block_setup(None).unwrap(), BlockSetup::Tpm2Luks);
494
495 assert!(install.get_block_setup(Some(BlockSetup::Direct)).is_err());
497 }
498
499 #[test]
500 fn test_arch() {
502 let env = EnvProperties {
504 sys_arch: "x86_64".to_string(),
505 };
506 let c: InstallConfigurationToplevel = toml::from_str(
507 r##"[install]
508root-fs-type = "xfs"
509"##,
510 )
511 .unwrap();
512 let mut install = c.install.unwrap();
513 let other = InstallConfigurationToplevel {
514 install: Some(InstallConfiguration {
515 kargs: Some(
516 ["console=tty0", "nosmt"]
517 .into_iter()
518 .map(ToOwned::to_owned)
519 .collect(),
520 ),
521 ..Default::default()
522 }),
523 };
524 install.merge(other.install.unwrap(), &env);
525 assert_eq!(
526 install.kargs,
527 Some(
528 ["console=tty0", "nosmt"]
529 .into_iter()
530 .map(ToOwned::to_owned)
531 .collect()
532 )
533 );
534 let env = EnvProperties {
535 sys_arch: "aarch64".to_string(),
536 };
537 let c: InstallConfigurationToplevel = toml::from_str(
538 r##"[install]
539root-fs-type = "xfs"
540"##,
541 )
542 .unwrap();
543 let mut install = c.install.unwrap();
544 let other = InstallConfigurationToplevel {
545 install: Some(InstallConfiguration {
546 kargs: Some(
547 ["console=tty0", "nosmt"]
548 .into_iter()
549 .map(ToOwned::to_owned)
550 .collect(),
551 ),
552 ..Default::default()
553 }),
554 };
555 install.merge(other.install.unwrap(), &env);
556 assert_eq!(
557 install.kargs,
558 Some(
559 ["console=tty0", "nosmt"]
560 .into_iter()
561 .map(ToOwned::to_owned)
562 .collect()
563 )
564 );
565
566 let env = EnvProperties {
568 sys_arch: "aarch64".to_string(),
569 };
570 let c: InstallConfigurationToplevel = toml::from_str(
571 r##"[install]
572root-fs-type = "xfs"
573"##,
574 )
575 .unwrap();
576 let mut install = c.install.unwrap();
577 let other = InstallConfigurationToplevel {
578 install: Some(InstallConfiguration {
579 kargs: Some(
580 ["console=ttyS0", "foo=bar"]
581 .into_iter()
582 .map(ToOwned::to_owned)
583 .collect(),
584 ),
585 match_architectures: Some(["x86_64"].into_iter().map(ToOwned::to_owned).collect()),
586 ..Default::default()
587 }),
588 };
589 install.merge(other.install.unwrap(), &env);
590 assert_eq!(install.kargs, None);
591 let other = InstallConfigurationToplevel {
592 install: Some(InstallConfiguration {
593 kargs: Some(
594 ["console=tty0", "nosmt"]
595 .into_iter()
596 .map(ToOwned::to_owned)
597 .collect(),
598 ),
599 match_architectures: Some(["aarch64"].into_iter().map(ToOwned::to_owned).collect()),
600 ..Default::default()
601 }),
602 };
603 install.merge(other.install.unwrap(), &env);
604 assert_eq!(
605 install.kargs,
606 Some(
607 ["console=tty0", "nosmt"]
608 .into_iter()
609 .map(ToOwned::to_owned)
610 .collect()
611 )
612 );
613
614 let env = EnvProperties {
616 sys_arch: "x86_64".to_string(),
617 };
618 let c: InstallConfigurationToplevel = toml::from_str(
619 r##"[install]
620root-fs-type = "xfs"
621"##,
622 )
623 .unwrap();
624 let mut install = c.install.unwrap();
625 let other = InstallConfigurationToplevel {
626 install: Some(InstallConfiguration {
627 kargs: Some(
628 ["console=tty0", "nosmt"]
629 .into_iter()
630 .map(ToOwned::to_owned)
631 .collect(),
632 ),
633 match_architectures: Some(
634 ["x86_64", "aarch64"]
635 .into_iter()
636 .map(ToOwned::to_owned)
637 .collect(),
638 ),
639 ..Default::default()
640 }),
641 };
642 install.merge(other.install.unwrap(), &env);
643 assert_eq!(
644 install.kargs,
645 Some(
646 ["console=tty0", "nosmt"]
647 .into_iter()
648 .map(ToOwned::to_owned)
649 .collect()
650 )
651 );
652 let env = EnvProperties {
653 sys_arch: "aarch64".to_string(),
654 };
655 let c: InstallConfigurationToplevel = toml::from_str(
656 r##"[install]
657root-fs-type = "xfs"
658"##,
659 )
660 .unwrap();
661 let mut install = c.install.unwrap();
662 let other = InstallConfigurationToplevel {
663 install: Some(InstallConfiguration {
664 kargs: Some(
665 ["console=tty0", "nosmt"]
666 .into_iter()
667 .map(ToOwned::to_owned)
668 .collect(),
669 ),
670 match_architectures: Some(
671 ["x86_64", "aarch64"]
672 .into_iter()
673 .map(ToOwned::to_owned)
674 .collect(),
675 ),
676 ..Default::default()
677 }),
678 };
679 install.merge(other.install.unwrap(), &env);
680 assert_eq!(
681 install.kargs,
682 Some(
683 ["console=tty0", "nosmt"]
684 .into_iter()
685 .map(ToOwned::to_owned)
686 .collect()
687 )
688 );
689 }
690
691 #[test]
692 fn test_parse_ostree() {
693 let env = EnvProperties {
694 sys_arch: "x86_64".to_string(),
695 };
696
697 let parse_cases = [
699 ("console=ttyS0", "console=ttyS0"),
700 ("console=ttyS0,115200n8", "console=ttyS0,115200n8"),
701 ("rd.lvm.lv=vg/root", "rd.lvm.lv=vg/root"),
702 ];
703 for (input, expected) in parse_cases {
704 let toml_str = format!(
705 r#"[install.ostree]
706bls-append-except-default = "{input}"
707"#
708 );
709 let c: InstallConfigurationToplevel = toml::from_str(&toml_str).unwrap();
710 assert_eq!(
711 c.install
712 .unwrap()
713 .ostree
714 .unwrap()
715 .bls_append_except_default
716 .unwrap(),
717 expected
718 );
719 }
720
721 let mut install: InstallConfiguration = toml::from_str(
723 r#"[ostree]
724bls-append-except-default = "console=ttyS0"
725"#,
726 )
727 .unwrap();
728 let other = InstallConfiguration {
729 ostree: Some(OstreeRepoOpts {
730 bls_append_except_default: Some("console=tty0".to_string()),
731 ..Default::default()
732 }),
733 ..Default::default()
734 };
735 install.merge(other, &env);
736 assert_eq!(
737 install.ostree.unwrap().bls_append_except_default.unwrap(),
738 "console=tty0"
739 );
740 }
741
742 #[test]
743 fn test_parse_stateroot() {
744 let c: InstallConfigurationToplevel = toml::from_str(
745 r#"[install]
746stateroot = "custom"
747"#,
748 )
749 .unwrap();
750 assert_eq!(c.install.unwrap().stateroot.unwrap(), "custom");
751 }
752
753 #[test]
754 fn test_merge_stateroot() {
755 let env = EnvProperties {
756 sys_arch: "x86_64".to_string(),
757 };
758 let mut install: InstallConfiguration = toml::from_str(
759 r#"stateroot = "original"
760"#,
761 )
762 .unwrap();
763 let other = InstallConfiguration {
764 stateroot: Some("newroot".to_string()),
765 ..Default::default()
766 };
767 install.merge(other, &env);
768 assert_eq!(install.stateroot.unwrap(), "newroot");
769 }
770
771 #[test]
772 fn test_parse_mount_specs() {
773 let c: InstallConfigurationToplevel = toml::from_str(
774 r#"[install]
775root-mount-spec = "LABEL=rootfs"
776boot-mount-spec = "UUID=abcd-1234"
777"#,
778 )
779 .unwrap();
780 let install = c.install.unwrap();
781 assert_eq!(install.root_mount_spec.unwrap(), "LABEL=rootfs");
782 assert_eq!(install.boot_mount_spec.unwrap(), "UUID=abcd-1234");
783 }
784
785 #[test]
786 fn test_merge_mount_specs() {
787 let env = EnvProperties {
788 sys_arch: "x86_64".to_string(),
789 };
790 let mut install: InstallConfiguration = toml::from_str(
791 r#"root-mount-spec = "UUID=old"
792boot-mount-spec = "UUID=oldboot"
793"#,
794 )
795 .unwrap();
796 let other = InstallConfiguration {
797 root_mount_spec: Some("LABEL=newroot".to_string()),
798 ..Default::default()
799 };
800 install.merge(other, &env);
801 assert_eq!(install.root_mount_spec.as_deref().unwrap(), "LABEL=newroot");
803 assert_eq!(install.boot_mount_spec.as_deref().unwrap(), "UUID=oldboot");
805 }
806
807 #[test]
810 fn test_parse_empty_mount_specs() {
811 let c: InstallConfigurationToplevel = toml::from_str(
812 r#"[install]
813root-mount-spec = ""
814boot-mount-spec = ""
815"#,
816 )
817 .unwrap();
818 let install = c.install.unwrap();
819 assert_eq!(install.root_mount_spec.as_deref().unwrap(), "");
820 assert_eq!(install.boot_mount_spec.as_deref().unwrap(), "");
821 }
822
823 #[test]
824 fn test_parse_bootupd_skip_boot_uuid() {
825 let c: InstallConfigurationToplevel = toml::from_str(
827 r#"[install.bootupd]
828skip-boot-uuid = true
829"#,
830 )
831 .unwrap();
832 assert_eq!(
833 c.install.unwrap().bootupd.unwrap().skip_boot_uuid.unwrap(),
834 true
835 );
836
837 let c: InstallConfigurationToplevel = toml::from_str(
839 r#"[install.bootupd]
840skip-boot-uuid = false
841"#,
842 )
843 .unwrap();
844 assert_eq!(
845 c.install.unwrap().bootupd.unwrap().skip_boot_uuid.unwrap(),
846 false
847 );
848
849 let c: InstallConfigurationToplevel = toml::from_str(
851 r#"[install]
852root-fs-type = "xfs"
853"#,
854 )
855 .unwrap();
856 assert!(c.install.unwrap().bootupd.is_none());
857 }
858
859 #[test]
860 fn test_merge_bootupd_skip_boot_uuid() {
861 let env = EnvProperties {
862 sys_arch: "x86_64".to_string(),
863 };
864 let mut install: InstallConfiguration = toml::from_str(
865 r#"[bootupd]
866skip-boot-uuid = false
867"#,
868 )
869 .unwrap();
870 let other = InstallConfiguration {
871 bootupd: Some(Bootupd {
872 skip_boot_uuid: Some(true),
873 }),
874 ..Default::default()
875 };
876 install.merge(other, &env);
877 assert_eq!(install.bootupd.unwrap().skip_boot_uuid.unwrap(), true);
879 }
880}
881
882#[test]
883fn test_parse_bootloader() {
884 let env = EnvProperties {
885 sys_arch: "x86_64".to_string(),
886 };
887
888 let c: InstallConfigurationToplevel = toml::from_str(
890 r##"[install]
891bootloader = "none"
892"##,
893 )
894 .unwrap();
895 assert_eq!(c.install.unwrap().bootloader, Some(Bootloader::None));
896
897 let c: InstallConfigurationToplevel = toml::from_str(
899 r##"[install]
900bootloader = "grub"
901"##,
902 )
903 .unwrap();
904 assert_eq!(c.install.unwrap().bootloader, Some(Bootloader::Grub));
905
906 let mut install: InstallConfiguration = toml::from_str(
909 r#"bootloader = "systemd"
910"#,
911 )
912 .unwrap();
913
914 let other = InstallConfiguration {
916 bootloader: Some(Bootloader::None),
917 ..Default::default()
918 };
919
920 install.merge(other, &env);
922 assert_eq!(install.bootloader, Some(Bootloader::None));
923}
924
925#[test]
926fn test_parse_discoverable_partitions() {
927 let c: InstallConfigurationToplevel = toml::from_str(
928 r##"[install]
929discoverable-partitions = true
930"##,
931 )
932 .unwrap();
933 assert_eq!(c.install.unwrap().discoverable_partitions, Some(true));
934
935 let c: InstallConfigurationToplevel = toml::from_str(
936 r##"[install]
937discoverable-partitions = false
938"##,
939 )
940 .unwrap();
941 assert_eq!(c.install.unwrap().discoverable_partitions, Some(false));
942
943 let c: InstallConfigurationToplevel = toml::from_str(
944 r##"[install]
945root-fs-type = "xfs"
946"##,
947 )
948 .unwrap();
949 assert_eq!(c.install.unwrap().discoverable_partitions, None);
950}
951
952#[test]
953fn test_parse_enforce_container_sigpolicy() {
954 let env = EnvProperties {
955 sys_arch: "x86_64".to_string(),
956 };
957
958 for (input, expected) in [("true", true), ("false", false)] {
960 let toml_str = format!(
961 r#"[install]
962enforce-container-sigpolicy = {input}
963"#
964 );
965 let c: InstallConfigurationToplevel = toml::from_str(&toml_str).unwrap();
966 assert_eq!(
967 c.install.unwrap().enforce_container_sigpolicy.unwrap(),
968 expected
969 );
970 }
971
972 let c: InstallConfigurationToplevel = toml::from_str(
974 r#"[install]
975root-fs-type = "xfs"
976"#,
977 )
978 .unwrap();
979 assert!(c.install.unwrap().enforce_container_sigpolicy.is_none());
980
981 let mut install: InstallConfiguration = toml::from_str(
983 r#"enforce-container-sigpolicy = false
984"#,
985 )
986 .unwrap();
987 let other = InstallConfiguration {
988 enforce_container_sigpolicy: Some(true),
989 ..Default::default()
990 };
991 install.merge(other, &env);
992 assert_eq!(install.enforce_container_sigpolicy.unwrap(), true);
993}