Skip to content

Commit 643eb74

Browse files
authored
properly record TUF artifacts we already have (#8571)
Two TUF repositories with different system versions can contain the same artifacts (e.g. Hubris archives, which have their own release cadence). When inserting a `TufRepoDescription`, we generate new IDs for all artifacts regardless of whether they already exist in the database; we then look for duplicates and avoid inserting already-present artifacts. But when those artifacts are associated with the repository in the `tuf_repo_artifact` table, we incorrectly use the ID generated for this upload and not the ID of the artifact in the database. This results in duplicate artifacts being "missing" from the repo description when pulled from the database.
1 parent 0c4b5a8 commit 643eb74

File tree

2 files changed

+40
-12
lines changed

2 files changed

+40
-12
lines changed

nexus/db-queries/src/db/datastore/update.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -384,23 +384,27 @@ async fn insert_impl(
384384
let mut new_artifacts = Vec::new();
385385
let mut all_artifacts = Vec::new();
386386

387-
enum ArtifactStatus {
387+
enum ArtifactStatus<'a> {
388388
New,
389-
Existing,
389+
Existing(&'a TufArtifact),
390390
Mismatch,
391391
}
392392

393-
impl ArtifactStatus {
394-
fn mark_existing(&mut self) {
393+
impl<'a> ArtifactStatus<'a> {
394+
fn mark_existing(&mut self, artifact: &'a TufArtifact) {
395395
match self {
396-
ArtifactStatus::New => *self = ArtifactStatus::Existing,
397-
ArtifactStatus::Existing | ArtifactStatus::Mismatch => (),
396+
ArtifactStatus::New => {
397+
*self = ArtifactStatus::Existing(artifact)
398+
}
399+
ArtifactStatus::Existing(_) | ArtifactStatus::Mismatch => {
400+
()
401+
}
398402
}
399403
}
400404

401405
fn mark_mismatch(&mut self) {
402406
match self {
403-
ArtifactStatus::New | ArtifactStatus::Existing => {
407+
ArtifactStatus::New | ArtifactStatus::Existing(_) => {
404408
*self = ArtifactStatus::Mismatch
405409
}
406410
ArtifactStatus::Mismatch => (),
@@ -413,7 +417,7 @@ async fn insert_impl(
413417
if let Some(&existing_nvk) =
414418
results_by_id.get(&uploaded_artifact.nvk())
415419
{
416-
status.mark_existing();
420+
status.mark_existing(existing_nvk);
417421
if existing_nvk.sha256 != uploaded_artifact.sha256
418422
|| existing_nvk.artifact_size()
419423
!= uploaded_artifact.artifact_size()
@@ -429,7 +433,7 @@ async fn insert_impl(
429433
if let Some(&existing_hash) = results_by_hash_id
430434
.get(&(&uploaded_artifact.kind, uploaded_artifact.sha256))
431435
{
432-
status.mark_existing();
436+
status.mark_existing(existing_hash);
433437
if existing_hash.name != uploaded_artifact.name
434438
|| existing_hash.version != uploaded_artifact.version
435439
{
@@ -447,8 +451,8 @@ async fn insert_impl(
447451
new_artifacts.push(uploaded_artifact.clone());
448452
all_artifacts.push(uploaded_artifact);
449453
}
450-
ArtifactStatus::Existing => {
451-
all_artifacts.push(uploaded_artifact);
454+
ArtifactStatus::Existing(existing_artifact) => {
455+
all_artifacts.push(existing_artifact.clone());
452456
}
453457
ArtifactStatus::Mismatch => {
454458
// This is an error case -- we'll return an error before
@@ -498,7 +502,7 @@ async fn insert_impl(
498502
use nexus_db_schema::schema::tuf_repo_artifact::dsl;
499503

500504
let mut values = Vec::new();
501-
for artifact in desc.artifacts.clone() {
505+
for artifact in &all_artifacts {
502506
slog::debug!(
503507
log,
504508
"inserting artifact into tuf_repo_artifact table";

nexus/tests/integration_tests/updates.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,30 @@ async fn test_repo_upload() -> Result<()> {
442442
serde_json::from_slice::<TufRepoInsertResponse>(&response.body)
443443
.context("error deserializing response body")?;
444444
assert_eq!(response.status, TufRepoInsertStatus::Inserted);
445+
let mut description = response.recorded;
446+
description.sort_artifacts();
447+
448+
// The artifacts should be exactly the same as the 1.0.0 repo we uploaded.
449+
assert_eq!(
450+
initial_description.artifacts, description.artifacts,
451+
"artifacts for 1.0.0 and 2.0.0 should match"
452+
);
453+
454+
// Now get the repository that was just uploaded and make sure the
455+
// artifact list is the same.
456+
let response: TufRepoGetResponse =
457+
make_get_request(client, "2.0.0".parse().unwrap(), StatusCode::OK)
458+
.execute()
459+
.await
460+
.context("error fetching repository")?
461+
.parsed_body()?;
462+
let mut get_description = response.description;
463+
get_description.sort_artifacts();
464+
465+
assert_eq!(
466+
description, get_description,
467+
"initial description matches fetched description"
468+
);
445469
}
446470
// No artifacts changed, so the generation number should still be 2...
447471
assert_eq!(

0 commit comments

Comments
 (0)