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
53 changes: 27 additions & 26 deletions src/shared/listeners/automatic_linkage.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,35 +268,36 @@ def build_new_links(container: Container) -> bool:

outcome = resolve_linkage_candidates(container)

proposal = CVEDerivationClusterProposal.objects.create(
cve=container.cve,
status=(
CVEDerivationClusterProposal.Status.REJECTED
with transaction.atomic():
proposal = CVEDerivationClusterProposal.objects.create(
cve=container.cve,
status=(
CVEDerivationClusterProposal.Status.REJECTED
if outcome.rejection
else CVEDerivationClusterProposal.Status.PENDING
),
rejection_reason=outcome.rejection.reason if outcome.rejection else None,
rejection_match_count=outcome.rejection.match_count or None
if outcome.rejection
else CVEDerivationClusterProposal.Status.PENDING
),
rejection_reason=outcome.rejection.reason if outcome.rejection else None,
rejection_match_count=outcome.rejection.match_count or None
if outcome.rejection
else None,
rejection_max_matches_limit=outcome.rejection.max_matches_limit or None
if outcome.rejection
else None,
algorithm_version=CVEDerivationClusterProposal.CURRENT_ALGORITHM_VERSION,
)

if outcome.derivations:
links = build_derivation_links(proposal, outcome.derivations)
DerivationClusterProposalLink.objects.bulk_create(links)
pkg_links = build_package_links(proposal, outcome.derivations)
PackageClusterProposalLink.objects.bulk_create(pkg_links)
logger.info(
"Matching suggestion for '%s': %d derivations, %d packages found.",
container.cve,
len(links),
len(pkg_links),
else None,
rejection_max_matches_limit=outcome.rejection.max_matches_limit or None
if outcome.rejection
else None,
algorithm_version=CVEDerivationClusterProposal.CURRENT_ALGORITHM_VERSION,
)

if outcome.derivations:
links = build_derivation_links(proposal, outcome.derivations)
DerivationClusterProposalLink.objects.bulk_create(links)
pkg_links = build_package_links(proposal, outcome.derivations)
PackageClusterProposalLink.objects.bulk_create(pkg_links)
logger.info(
"Matching suggestion for '%s': %d derivations, %d packages found.",
container.cve,
len(links),
len(pkg_links),
)

return True


Expand Down
20 changes: 20 additions & 0 deletions src/shared/tests/test_linkage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from collections.abc import Callable
from datetime import timedelta
from unittest import mock

import pytest
from django.test import override_settings
Expand Down Expand Up @@ -549,3 +550,22 @@ def test_package_link_provenance_flags_merged_across_drvs(
link.provenance_flags
== ProvenanceFlags.PACKAGE_NAME_MATCH | ProvenanceFlags.PRODUCT_MATCH
)


def test_build_new_links_is_atomic(
make_container: Callable[..., Container],
make_drv: Callable[..., NixDerivation],
) -> None:
make_drv(pname="foo")
container = make_container(package_name="foo")

with mock.patch.object(
DerivationClusterProposalLink.objects,
"bulk_create",
side_effect=Exception("simulated DB failure"),
):
with pytest.raises(Exception, match="simulated DB failure"):
build_new_links(container)

# A retry must succeed after a failed first attempt
assert build_new_links(container)