Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions artifact/src/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,21 +112,34 @@ impl ArtifactKind {
pub const SWITCH_ROT_IMAGE_B: Self =
Self::from_static("switch_rot_image_b");

/// Host phase 1 identifier.
/// Gimlet Host phase 1 identifier.
///
/// Derived from [`KnownArtifactKind::Host`].
pub const HOST_PHASE_1: Self = Self::from_static("host_phase_1");
pub const GIMLET_HOST_PHASE_1: Self =
Self::from_static("gimlet_host_phase_1");
Comment on lines +115 to +119
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting that if we had any long-running systems that had imported updates into the database, changing this name would break those. But we don't so this is fine.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah my hope is to make any changes we need right now so we don't run into that later


/// Cosmo Host phase 1 identifier.
///
/// Derived from [`KnownArtifactKind::Host`].
pub const COSMO_HOST_PHASE_1: Self =
Self::from_static("cosmo_host_phase_1");

/// Host phase 2 identifier.
///
/// Derived from [`KnownArtifactKind::Host`].
pub const HOST_PHASE_2: Self = Self::from_static("host_phase_2");

/// Trampoline phase 1 identifier.
/// Gimlet Trampoline phase 1 identifier.
///
/// Derived from [`KnownArtifactKind::Trampoline`].
pub const GIMLET_TRAMPOLINE_PHASE_1: Self =
Self::from_static("gimlet_trampoline_phase_1");

/// Cosmo Trampoline phase 1 identifier.
///
/// Derived from [`KnownArtifactKind::Trampoline`].
pub const TRAMPOLINE_PHASE_1: Self =
Self::from_static("trampoline_phase_1");
pub const COSMO_TRAMPOLINE_PHASE_1: Self =
Self::from_static("cosmo_trampoline_phase_1");

/// Trampoline phase 2 identifier.
///
Expand Down
6 changes: 4 additions & 2 deletions bin/manifests/fake-non-semver.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,17 @@ name = "fake-host"
version = "2.0.0"
[artifact.host.source]
kind = "composite-host"
phase_1 = { kind = "fake", size = "512KiB" }
gimlet_phase_1 = { kind = "fake", size = "512KiB" }
cosmo_phase_1 = { kind = "fake", size = "512KiB" }
phase_2 = { kind = "fake", size = "1MiB" }

[[artifact.trampoline]]
name = "fake-trampoline"
version = "non-semver"
[artifact.trampoline.source]
kind = "composite-host"
phase_1 = { kind = "fake", size = "512KiB" }
gimlet_phase_1 = { kind = "fake", size = "512KiB" }
cosmo_phase_1 = { kind = "fake", size = "512KiB" }
phase_2 = { kind = "fake", size = "1MiB" }

[[artifact.control_plane]]
Expand Down
6 changes: 4 additions & 2 deletions bin/manifests/fake.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,17 @@ name = "fake-host"
version = "1.0.0"
[artifact.host.source]
kind = "composite-host"
phase_1 = { kind = "fake", size = "512KiB" }
gimlet_phase_1 = { kind = "fake", size = "512KiB" }
cosmo_phase_1 = { kind = "fake", size = "512KiB" }
phase_2 = { kind = "fake", size = "1MiB" }

[[artifact.trampoline]]
name = "fake-trampoline"
version = "1.0.0"
[artifact.trampoline.source]
kind = "composite-host"
phase_1 = { kind = "fake", size = "512KiB" }
gimlet_phase_1 = { kind = "fake", size = "512KiB" }
cosmo_phase_1 = { kind = "fake", size = "512KiB" }
phase_2 = { kind = "fake", size = "1MiB" }

[[artifact.control_plane]]
Expand Down
107 changes: 86 additions & 21 deletions lib/src/artifact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,30 @@ impl TempWrittenArtifact {
}
}

pub(crate) fn make_filler_text_with_seed(
// composite artifact.
kind: &str,
version: &ArtifactVersion,
length: usize,
seed: &str,
) -> Vec<u8> {
// Add the kind and version to the filler text first. This ensures that
// hashes are unique by kind and version.
let mut out = Vec::with_capacity(length);
out.extend_from_slice(kind.as_bytes());
out.extend_from_slice(b":");
out.extend_from_slice(version.as_str().as_bytes());
out.extend_from_slice(b":");
out.extend_from_slice(seed.as_bytes());
out.extend_from_slice(b":");
let remaining = length.saturating_sub(out.len());
out.extend(
std::iter::repeat(FILLER_TEXT).flatten().copied().take(remaining),
);

out
}

pub(crate) fn make_filler_text(
// composite artifact.
kind: &str,
Expand Down Expand Up @@ -249,33 +273,52 @@ pub(crate) fn make_filler_text(
/// tarballs.
#[derive(Clone, Debug)]
pub struct HostPhaseImages {
pub phase_1: Bytes,
pub gimlet_phase_1: Bytes,
pub cosmo_phase_1: Bytes,
pub phase_2: Bytes,
}

/// File sources for extraction
///
/// Passing three identical arguments gets confusing and error prone
pub struct HostPhaseImageSource<W: io::Write> {
pub gimlet_phase_1: W,
pub cosmo_phase_1: W,
pub phase_2: W,
}

impl HostPhaseImages {
pub fn extract<R: io::BufRead>(reader: R) -> Result<Self> {
let mut phase_1 = Vec::new();
let mut gimlet_phase_1 = Vec::new();
let mut cosmo_phase_1 = Vec::new();
let mut phase_2 = Vec::new();
Self::extract_into(
reader,
io::Cursor::<&mut Vec<u8>>::new(&mut phase_1),
io::Cursor::<&mut Vec<u8>>::new(&mut phase_2),
)?;
Ok(Self { phase_1: phase_1.into(), phase_2: phase_2.into() })
let source = HostPhaseImageSource {
gimlet_phase_1: io::Cursor::<&mut Vec<u8>>::new(
&mut gimlet_phase_1,
),
cosmo_phase_1: io::Cursor::<&mut Vec<u8>>::new(&mut cosmo_phase_1),
phase_2: io::Cursor::<&mut Vec<u8>>::new(&mut phase_2),
};

Self::extract_into(reader, source)?;
Ok(Self {
gimlet_phase_1: gimlet_phase_1.into(),
cosmo_phase_1: cosmo_phase_1.into(),
phase_2: phase_2.into(),
})
}

pub fn extract_into<R: io::BufRead, W: io::Write>(
reader: R,
phase_1: W,
phase_2: W,
source: HostPhaseImageSource<W>,
) -> Result<()> {
let uncompressed = flate2::bufread::GzDecoder::new(reader);
let mut archive = tar::Archive::new(uncompressed);

let mut oxide_json_found = false;
let mut phase_1_writer = Some(phase_1);
let mut phase_2_writer = Some(phase_2);
let mut gimlet_phase_1_writer = Some(source.gimlet_phase_1);
let mut cosmo_phase_1_writer = Some(source.cosmo_phase_1);
let mut phase_2_writer = Some(source.phase_2);
for entry in archive
.entries()
.context("error building list of entries from archive")?
Expand All @@ -300,9 +343,21 @@ impl HostPhaseImages {
)
}
oxide_json_found = true;
} else if path == Path::new(HOST_PHASE_1_FILE_NAME) {
if let Some(phase_1) = phase_1_writer.take() {
read_entry_into(entry, HOST_PHASE_1_FILE_NAME, phase_1)?;
} else if path == Path::new(COSMO_HOST_PHASE_1_FILE_NAME) {
if let Some(cosmo_phase_1) = cosmo_phase_1_writer.take() {
read_entry_into(
entry,
COSMO_HOST_PHASE_1_FILE_NAME,
cosmo_phase_1,
)?;
}
} else if path == Path::new(GIMLET_HOST_PHASE_1_FILE_NAME) {
if let Some(gimlet_phase_1) = gimlet_phase_1_writer.take() {
read_entry_into(
entry,
GIMLET_HOST_PHASE_1_FILE_NAME,
gimlet_phase_1,
)?;
}
} else if path == Path::new(HOST_PHASE_2_FILE_NAME) {
if let Some(phase_2) = phase_2_writer.take() {
Expand All @@ -311,7 +366,8 @@ impl HostPhaseImages {
}

if oxide_json_found
&& phase_1_writer.is_none()
&& gimlet_phase_1_writer.is_none()
&& cosmo_phase_1_writer.is_none()
&& phase_2_writer.is_none()
{
break;
Expand All @@ -325,8 +381,11 @@ impl HostPhaseImages {

// If we didn't `.take()` the writer out of the options, we never saw
// the expected phase1/phase2 filenames.
if phase_1_writer.is_some() {
not_found.push(HOST_PHASE_1_FILE_NAME);
if cosmo_phase_1_writer.is_some() {
not_found.push(COSMO_HOST_PHASE_1_FILE_NAME);
}
if gimlet_phase_1_writer.is_some() {
not_found.push(GIMLET_HOST_PHASE_1_FILE_NAME);
}
if phase_2_writer.is_some() {
not_found.push(HOST_PHASE_2_FILE_NAME);
Expand All @@ -339,8 +398,13 @@ impl HostPhaseImages {
Ok(())
}

pub fn phase_1_hash(&self) -> ArtifactHash {
let hash = Sha256::digest(&self.phase_1);
pub fn gimlet_phase_1_hash(&self) -> ArtifactHash {
let hash = Sha256::digest(&self.gimlet_phase_1);
ArtifactHash(hash.into())
}

pub fn cosmo_phase_1_hash(&self) -> ArtifactHash {
let hash = Sha256::digest(&self.cosmo_phase_1);
ArtifactHash(hash.into())
}

Expand Down Expand Up @@ -559,7 +623,8 @@ impl ControlPlaneZoneImages {

static FILLER_TEXT: &[u8; 16] = b"tufaceousfaketxt";
static OXIDE_JSON_FILE_NAME: &str = "oxide.json";
pub(crate) static HOST_PHASE_1_FILE_NAME: &str = "image/rom";
pub(crate) static GIMLET_HOST_PHASE_1_FILE_NAME: &str = "image/gimlet.rom";
pub(crate) static COSMO_HOST_PHASE_1_FILE_NAME: &str = "image/cosmo.rom";
pub(crate) static HOST_PHASE_2_FILE_NAME: &str = "image/zfs.img";
pub(crate) static ROT_ARCHIVE_A_FILE_NAME: &str = "archive-a.zip";
pub(crate) static ROT_ARCHIVE_B_FILE_NAME: &str = "archive-b.zip";
Expand Down
16 changes: 12 additions & 4 deletions lib/src/artifact/composite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ use tufaceous_artifact::ArtifactHash;
use tufaceous_brand_metadata::{ArchiveType, Metadata};

use super::{
CONTROL_PLANE_ARCHIVE_ZONE_DIRECTORY, HOST_PHASE_1_FILE_NAME,
HOST_PHASE_2_FILE_NAME, ROT_ARCHIVE_A_FILE_NAME, ROT_ARCHIVE_B_FILE_NAME,
CONTROL_PLANE_ARCHIVE_ZONE_DIRECTORY, COSMO_HOST_PHASE_1_FILE_NAME,
GIMLET_HOST_PHASE_1_FILE_NAME, HOST_PHASE_2_FILE_NAME,
ROT_ARCHIVE_A_FILE_NAME, ROT_ARCHIVE_B_FILE_NAME,
};

/// Represents a single entry in a composite artifact.
Expand Down Expand Up @@ -104,11 +105,18 @@ impl<W: Write> CompositeHostArchiveBuilder<W> {
Ok(Self { inner })
}

pub fn append_phase_1(
pub fn append_gimlet_phase_1(
&mut self,
entry: CompositeEntry<'_>,
) -> Result<ArtifactHash> {
self.inner.append_file(HOST_PHASE_1_FILE_NAME, entry)
self.inner.append_file(GIMLET_HOST_PHASE_1_FILE_NAME, entry)
}

pub fn append_cosmo_phase_1(
&mut self,
entry: CompositeEntry<'_>,
) -> Result<ArtifactHash> {
self.inner.append_file(COSMO_HOST_PHASE_1_FILE_NAME, entry)
}

pub fn append_phase_2(
Expand Down
Loading
Loading