Skip to content
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

Allow different versions for differently signed RoT images #5580

Merged
merged 2 commits into from
May 20, 2024

Conversation

labbott
Copy link
Contributor

@labbott labbott commented Apr 19, 2024

Currently we restrict all RoT archives for {Gimlet, PSC, Sidecar} to be the same version. This restricts us to having staging and production images to be the same version. What we actually want is all archives for a particular kind signed with the same key to have the same version.

@labbott labbott marked this pull request as draft April 19, 2024 19:31
@labbott labbott force-pushed the staging_and_prod_diff branch from b557ccf to 5cd0db8 Compare April 19, 2024 20:01
Copy link
Contributor

@sunshowers sunshowers left a comment

Choose a reason for hiding this comment

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

Had a quick look, generally looks good!

// version at builder time (builder time is not async)
let image_a = rot_a_data
.reader_stream()
.and_then(|stream| async {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this use a .await rather than a future combinator? In general, .await is preferable -- it's easier to understand, particularly because .and_then exists both on futures and on results. (There are exceptions where future combinators make sense, but this doesn't seem to be one of them.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was going off of what wicketd was doing (https://github.com/oxidecomputer/omicron/blob/main/wicketd/src/update_tracker.rs#L1679-L1696) but I'm fine with changing it for readability.

tokio_util::io::StreamReader::new(stream)
.read_to_end(&mut buf)
.await
.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

These are extracted as files on disk, so I believe they can produce errors. Even if they can't, it would be good to replace the unwrap() with an expect("justification").

Copy link
Contributor Author

Choose a reason for hiding this comment

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

wicketd mentions this can be an I/O error https://github.com/oxidecomputer/omicron/blob/main/wicketd/src/update_tracker.rs#L1690 . I'll match that here.

Comment on lines 349 to 358
.and_then(|stream| async {
let mut buf = Vec::with_capacity(rot_b_data.file_size());
tokio_util::io::StreamReader::new(stream)
.read_to_end(&mut buf)
.await
.unwrap();
Ok(buf)
})
.await
.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comments about both using .await and bubbling up errors here.

Comment on lines +964 to +980
let archive_a = make_fake_rot_image(sign, board);
let archive_b = make_fake_rot_image(sign, board);
Copy link
Contributor

Choose a reason for hiding this comment

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

haha I was wondering when this would become less random, thanks for doing this :)

@labbott labbott force-pushed the staging_and_prod_diff branch from 5cd0db8 to bd58a59 Compare April 30, 2024 14:47
Comment on lines +143 to +144
#[error("error creating reader stream")]
CreateReaderStream(#[source] anyhow::Error),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure the opinion on adding new errors here

Copy link
Contributor

Choose a reason for hiding this comment

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

Adding new errors (when needed) is good 👍

@labbott labbott marked this pull request as ready for review May 13, 2024 16:01
Comment on lines +143 to +144
#[error("error creating reader stream")]
CreateReaderStream(#[source] anyhow::Error),
Copy link
Contributor

Choose a reason for hiding this comment

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

Adding new errors (when needed) is good 👍

tokio_util::io::StreamReader::new(image_a_stream)
.read_to_end(&mut image_a)
.await
.expect("failed to read RoT image a");
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we can .expect() these - reader_stream() returns streams that are backed by tokio::fs::Files, which can fail at runtime. Probably need something like a

RepositoryError::ReadExtractedArchive {
    artifact: ArtifactHashId,
    #[source]
    error: std::io::Error,
}

variant to use here?

update-common/src/artifacts/update_plan.rs Show resolved Hide resolved
return Err(RepositoryError::ReadHubrisCabooseBoard { id, error });
}
};
let sign = match std::str::from_utf8(sign) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we use the fact that we're storing sign as a String anywhere? I'm wondering if this conversion is necessary at all, or if we could store (ArtifactId, Vec<u8>) instead.

Copy link
Contributor

@sunshowers sunshowers left a comment

Choose a reason for hiding this comment

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

General logic looks great! Just a few comments + co-signing John's comments.

tokio_util::io::StreamReader::new(image_b_stream)
.read_to_end(&mut image_b)
.await
.expect("failed to read RoT image b");
Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah same here -- we should produce an error.

let logctx = test_setup_log("test_multi_rot_version");

let mut plan_builder =
UpdatePlanBuilder::new("0.0.0".parse().unwrap(), &logctx.log)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this use VERSION_0 as well?

@@ -112,6 +113,7 @@ pub struct UpdatePlanBuilder<'a> {
// The by_id and by_hash maps, and metadata, used in `ArtifactsWithPlan`.
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment should be updated.

@@ -112,6 +113,7 @@ pub struct UpdatePlanBuilder<'a> {
// The by_id and by_hash maps, and metadata, used in `ArtifactsWithPlan`.
by_id: BTreeMap<ArtifactId, Vec<ArtifactHashId>>,
by_hash: HashMap<ArtifactHashId, ExtractedArtifactDataHandle>,
by_sign: HashMap<(KnownArtifactKind, String), Vec<ArtifactId>>,
Copy link
Contributor

Choose a reason for hiding this comment

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

What does the key stand for?

What do you think about turning this into a struct with named fields?

by_sign seems to only contain Hubris artifacts, is that right? If so could this be documented here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll clarify this with some rework and comments

return Err(RepositoryError::ReadHubrisCaboose { id, error });
}
};
let sign = match caboose.sign() {
Copy link
Contributor

Choose a reason for hiding this comment

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

hmm, what kind of values does sign have? Is it one of a small fixed set of values, or is it arbitrary?

is it worth wrapping the sign in a newtype (probably in the caboose API I guess)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a &[u8] (https://github.com/oxidecomputer/hubtools/blob/main/hubtools/src/caboose.rs#L47-L49) although as @jgallagher noted below the extra step of conversion to a string is probably unnecessary except for error cases.

Currently we restrict all RoT archives for {Gimlet, PSC, Sidecar}
to be the same version. This restricts us to having staging and
production images to be the same version. What we actually want
is all archives for a particular kind _signed with the same key_
to have the same version.
@labbott labbott force-pushed the staging_and_prod_diff branch 3 times, most recently from 24de5b0 to e7c16a0 Compare May 20, 2024 14:39
@labbott labbott force-pushed the staging_and_prod_diff branch from e7c16a0 to 463aa0d Compare May 20, 2024 15:13
Copy link
Contributor

@jgallagher jgallagher left a comment

Choose a reason for hiding this comment

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

LGTM 👍

Copy link
Contributor

@sunshowers sunshowers left a comment

Choose a reason for hiding this comment

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

agreed, looks great!

@labbott labbott merged commit 94ef8e5 into main May 20, 2024
18 checks passed
@labbott labbott deleted the staging_and_prod_diff branch May 20, 2024 20:27
labbott added a commit that referenced this pull request Jun 7, 2024
…5580)"

This reverts commit 94ef8e5.

It turns out there are several places that are still making assumptions
about versions. Until we've identified them all just revert this change
to avoid any unexpected surprises.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants