lanzaboote/rust/lanzatool/src/generation.rs

95 lines
2.8 KiB
Rust
Raw Normal View History

use serde::{Deserialize, Serialize};
2022-11-26 08:55:15 -05:00
use std::fmt;
use std::fs;
use std::path::{Path, PathBuf};
2022-11-26 08:55:15 -05:00
use anyhow::{Context, Result};
use bootspec::BootJson;
use bootspec::SpecialisationName;
use bootspec::generation::Generation;
// TODO: actually, I'm not sure it's a good thing to have Default
// we should maybe have TryDefault?
// discuss this with upstream.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct SecureBootExtension {
#[serde(rename="org.lanzaboote.osRelease")]
pub os_release: PathBuf
}
2022-11-26 08:55:15 -05:00
pub type ExtendedBootJson = BootJson<SecureBootExtension>;
2022-11-26 08:55:15 -05:00
#[derive(Debug)]
pub struct OSGeneration {
/// Top-level nixpkgs version
version: u64,
/// Top-level specialisation name
specialisation_name: Option<SpecialisationName>,
/// Top-level bootspec document
pub bootspec: ExtendedBootJson,
}
fn into_boot_json(generation: Generation<SecureBootExtension>) -> Result<ExtendedBootJson> {
Ok(match generation {
Generation::V1(json) => json,
_ => panic!("Failed")
})
}
2022-11-26 08:55:15 -05:00
impl OSGeneration {
2022-11-26 08:55:15 -05:00
pub fn from_toplevel(toplevel: impl AsRef<Path>) -> Result<Self> {
let bootspec_path = toplevel.as_ref().join("bootspec/boot.json");
let generation: Generation<SecureBootExtension> = serde_json::from_slice(
2022-11-26 17:19:08 -05:00
&fs::read(bootspec_path).context("Failed to read bootspec file")?,
)
.context("Failed to parse bootspec json")?;
Ok(Self {
version: parse_version(toplevel)?,
2022-11-27 05:19:02 -05:00
specialisation_name: None,
bootspec: into_boot_json(generation)?,
})
2022-11-26 08:55:15 -05:00
}
2022-11-27 05:19:02 -05:00
pub fn specialise(&self, name: &SpecialisationName, bootspec: &ExtendedBootJson) -> Self {
2022-11-27 05:19:02 -05:00
Self {
version: self.version,
specialisation_name: Some(name.clone()),
bootspec: bootspec.clone()
2022-11-27 05:19:02 -05:00
}
}
pub fn is_specialized(&self) -> Option<SpecialisationName> {
2022-11-27 05:19:02 -05:00
self.specialisation_name.clone()
}
2022-11-26 08:55:15 -05:00
}
impl fmt::Display for OSGeneration {
2022-11-26 08:55:15 -05:00
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.version)
2022-11-26 08:55:15 -05:00
}
}
fn parse_version(toplevel: impl AsRef<Path>) -> Result<u64> {
2022-11-26 17:19:08 -05:00
let file_name = toplevel
.as_ref()
.file_name()
.ok_or_else(|| anyhow::anyhow!("Failed to extract file name from generation"))?;
let file_name_str = file_name
.to_str()
.with_context(|| "Failed to convert file name of generation to string")?;
let generation_version = file_name_str
2022-11-26 17:19:08 -05:00
.split('-')
.nth(1)
2022-11-26 17:19:08 -05:00
.ok_or_else(|| anyhow::anyhow!("Failed to extract version from generation"))?;
let parsed_generation_version = generation_version
.parse()
.with_context(|| format!("Failed to parse generation version: {}", generation_version))?;
Ok(parsed_generation_version)
}