-
Notifications
You must be signed in to change notification settings - Fork 15
engineering: Improve COSI Metadata Parsing #394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR improves COSI metadata parsing by enhancing type safety and forward compatibility. The changes introduce strongly-typed enums for bootloader types and versions, replace string comparisons with enum matching, and remove deny_unknown_fields to allow graceful handling of future metadata fields.
Key changes:
- Introduced
KnownMetadataVersionenum to track supported COSI specification versions - Replaced string-based bootloader type checks with
BootloaderTypeandSystemdBootLoaderTypeenums - Removed
deny_unknown_fieldsfrom all structs for forward compatibility
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| // Valid combination | ||
| _ => {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the wildcard for all non-error conditions, this whole statement is only trying to match on error conditions
|
|
||
| // Unknown bootloader type is invalid | ||
| (BootloaderType::Unknown(bootloader_type), _) => { | ||
| bail!("Unsupported bootloader type: {}", bootloader_type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should only bail if the version is equal to 1.1. Otherwise we can't add new bootloader types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is more about this version of trident not recognizing this bootloader type. If we see something we don't recognize then we have no idea what to do with it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd feel better about running this once we know the specific operation we're doing with the COSI. Perhaps directly in the validate() method of the subsystem that is using the bootloader field?
For something like trident stream-image it might not matter and would present a forward compatibility concern
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair. This is the existing logic that was implemented before, but happy to revise for forward compat.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did some extra future proofing, basically we will always validate the metadata as long as it's technically valid, regardless of the support offered by the version of Trident.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #[serde(untagged)] | ||
| Unknown(String), | ||
| } | ||
|
|
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The BootloaderType enum should derive Display like SystemdBootloaderType does (line 367) for consistency and to enable more ergonomic error messages. Currently, line 148 uses the inner String value from the Unknown variant, but with Display, the enum could be used directly in format strings for all variants.
| use std::fmt; | |
| impl fmt::Display for BootloaderType { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| match self { | |
| BootloaderType::SystemdBoot => write!(f, "systemd-boot"), | |
| BootloaderType::Grub => write!(f, "grub"), | |
| BootloaderType::Unknown(s) => write!(f, "{s}"), | |
| } | |
| } | |
| } |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| match systemd_boot.entries.as_slice() { | ||
| // No entries is invalid | ||
| [] => bail!("Bootloader type 'systemd-boot' must not be empty"), | ||
|
|
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's trailing whitespace on line 129 after the pattern match. Consider removing it for code cleanliness.
| impl PartialOrd for MetadataVersion { | ||
| fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
| Some(self.cmp(other)) | ||
| } | ||
| } | ||
|
|
||
| impl Ord for MetadataVersion { | ||
| fn cmp(&self, other: &Self) -> Ordering { | ||
| match self.major.cmp(&other.major) { | ||
| Ordering::Equal => self.minor.cmp(&other.minor), | ||
| ord => ord, | ||
| } | ||
| } | ||
| } |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new Ord and PartialOrd implementations for MetadataVersion lack test coverage. Consider adding tests to verify the comparison behavior works correctly, especially for edge cases like comparing different major versions and different minor versions.
| pub(crate) enum BootloaderType { | ||
| #[serde(rename = "systemd-boot")] | ||
| SystemdBoot, | ||
|
|
||
| #[serde(rename = "grub")] | ||
| Grub, | ||
|
|
||
| #[serde(untagged)] | ||
| Unknown(String), | ||
| } |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new BootloaderType enum and its deserialization behavior (especially the Unknown variant) lacks test coverage. Consider adding tests to verify that known bootloader types deserialize correctly and unknown types are properly captured in the Unknown variant.
| pub(crate) enum SystemdBootloaderType { | ||
| #[serde(rename = "uki-standalone")] | ||
| #[strum(to_string = "uki-standalone")] | ||
| UkiStandalone, | ||
|
|
||
| #[serde(rename = "uki-config")] | ||
| #[strum(to_string = "uki-config")] | ||
| UkiConfig, | ||
|
|
||
| #[serde(rename = "config")] | ||
| #[strum(to_string = "config")] | ||
| Config, | ||
|
|
||
| #[serde(untagged)] | ||
| #[strum(to_string = "{0}")] | ||
| Unknown(String), | ||
| } |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new SystemdBootloaderType enum and its deserialization behavior (especially the Unknown variant and the Display trait) lacks test coverage. Consider adding tests to verify that known boot types deserialize correctly, unknown types are captured properly, and the Display trait produces the expected string representations.
| // Validate bootloader on COSI version >= 1.1 | ||
| if self.version >= KnownMetadataVersion::V1_1.as_version() { | ||
| let Some(bootloader) = self.bootloader.as_ref() else { | ||
| bail!("Bootloader metadata is required for COSI version >= 1.1, but not provided"); | ||
| }; | ||
|
|
||
| match (&bootloader.bootloader_type, &bootloader.systemd_boot) { | ||
| // Grub with systemd-boot entries is invalid | ||
| (BootloaderType::Grub, Some(_)) => { | ||
| bail!("Bootloader type 'grub' cannot have systemd-boot entries"); | ||
| } | ||
|
|
||
| // Systemd-boot without entries is invalid | ||
| (BootloaderType::SystemdBoot, None) => { | ||
| bail!("Bootloader type 'systemd-boot' requires systemd-boot entries"); | ||
| } | ||
|
|
||
| // Systemd-boot with not exactly 1 UKI entry is invalid for this version of Trident | ||
| (BootloaderType::SystemdBoot, Some(systemd_boot)) => { | ||
| match systemd_boot.entries.as_slice() { | ||
| // No entries is invalid | ||
| [] => bail!("Bootloader type 'systemd-boot' must not be empty"), | ||
|
|
||
| // More than one entry, is not allowed in this version of Trident. | ||
| [_, _, ..] => { | ||
| bail!("Multiple bootloader entries are not supported for bootloader type 'systemd-boot' in this version of Trident"); | ||
| } | ||
|
|
||
| // One entry of type other than uki-standalone is invalid | ||
| [entry] if !entry.boot_type.eq(&SystemdBootloaderType::UkiStandalone) => bail!( | ||
| "Bootloader type 'systemd-boot' only supports 'uki-standalone' entry type, found: {}", | ||
| entry.boot_type | ||
| ), | ||
|
|
||
| // Exactly one uki-standalone entry is valid | ||
| [ _ ] => {} | ||
| } | ||
| _ => bail!("Unsupported bootloader type: {}", bootloader_type), | ||
| } | ||
| } | ||
| None => { | ||
| if self.version.major > 1 || (self.version.major == 1 && self.version.minor > 0) { | ||
| bail!("Bootloader is required for COSI version >= 1.1, but not provided"); | ||
|
|
||
| // Unknown bootloader type is invalid | ||
| (BootloaderType::Unknown(bootloader_type), _) => { | ||
| bail!("Unsupported bootloader type: {}", bootloader_type); | ||
| } | ||
|
|
||
| // Valid combination | ||
| _ => {} | ||
| } | ||
| } |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The refactored bootloader validation logic lacks test coverage for the various edge cases, including: validation with COSI version >= 1.1, Grub with systemd-boot entries, systemd-boot without entries, systemd-boot with empty entries, systemd-boot with multiple entries, systemd-boot with non-UKI entry types, and unknown bootloader types. Consider adding comprehensive tests for these validation scenarios.
🔍 Description