Skip to content

Commit

Permalink
Teach merge_advisory() to use its own copies of relevant UpdateCollec…
Browse files Browse the repository at this point in the history
…tions.

Added migration to clean up and data that is already in this broken state.

fixes #7291
[nocoverage]
  • Loading branch information
ggainey committed Aug 10, 2020
1 parent 4ac1763 commit 192a903
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGES/7291.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Prevented advisory-merge from 'reusing' UpdateCollections from the merging advisories.
33 changes: 30 additions & 3 deletions pulp_rpm/app/advisory.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
)

from pulp_rpm.app.exceptions import AdvisoryConflict
from pulp_rpm.app.models import UpdateRecord
from pulp_rpm.app.models import (
UpdateCollectionPackage,
UpdateRecord,
)
from pulp_rpm.app.shared_utils import is_previous_version


Expand Down Expand Up @@ -190,6 +193,26 @@ def resolve_advisory_conflict(previous_advisory, added_advisory):
return to_add, to_remove, to_exclude


def _copy_update_collections_for(collections):
"""
Deep-copy each UpdateCollection in the_collections, and its UpdateCollectionPackages.
"""
new_collections = []
with transaction.atomic():
for collection in collections.all().iterator():
uc_packages = collection.packages.all()
collection.pk = None
collection.save()
new_packages = []
for a_package in uc_packages:
a_package.pk = None
a_package.update_collection = collection
new_packages.append(a_package)
UpdateCollectionPackage.objects.bulk_create(new_packages)
new_collections.append(collection)
return new_collections


def merge_advisories(previous_advisory, added_advisory):
"""
Create a new advisory with the merged pkglist.
Expand All @@ -204,8 +227,10 @@ def merge_advisories(previous_advisory, added_advisory):
package list from the other two ones.
"""
previous_collections = previous_advisory.collections.all()
added_collections = added_advisory.collections.all()
# For UpdateCollections, make sure we don't re-use the collections for either of the
# advisories being merged
previous_collections = _copy_update_collections_for(previous_advisory.collections)
added_collections = _copy_update_collections_for(added_advisory.collections)
references = previous_advisory.references.all()

with transaction.atomic():
Expand All @@ -232,6 +257,8 @@ def merge_advisories(previous_advisory, added_advisory):
collections=chain(previous_collections, added_collections))
merged_digest = hash_update_record(merged_advisory_cr)
merged_advisory = previous_advisory
# Need to null both pk (content_ptr_id) and pulp_id here to insure django doesn't
# find the original advisory instead of making a copy for us
merged_advisory.pk = None
merged_advisory.pulp_id = None
merged_advisory.digest = merged_digest
Expand Down
51 changes: 51 additions & 0 deletions pulp_rpm/app/migrations/0017_merge_advisory_collections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from django.db import (
migrations,
models,
)


def do_clone(apps, advisory, in_collection):
# We're cloning collection - find its updatecollectionpackages
uc_packages = in_collection.packages.all()
# break the advisory/collection link
in_collection.update_record.remove(advisory)
# create a new copy of the collection and link it to the advisory
new_collection = in_collection
new_collection.pk = None
new_collection.save()
# need to have an id before we can build the m2m relation
new_collection.update_record.add(advisory)
new_collection.save()
new_packages = []
for a_package in uc_packages.iterator():
# create copies of the package list and link to new collection
a_package.pk = None
a_package.update_collection = new_collection
new_packages.append(a_package)
UpdateCollectionPackage = apps.get_model("rpm", "UpdateCollectionPackage")
UpdateCollectionPackage.objects.bulk_create(new_packages)


def clone_reused_update_collections(apps, schema):
# Find UpdateCollections that point to multiple UpdateRecords
# For all but the first one, create a clone
UpdateCollection = apps.get_model("rpm", "UpdateCollection")
collections = UpdateCollection.objects.annotate(
num_advisories=models.Count('update_record')).filter(
num_advisories__gte=2).all().iterator()
for collection in collections:
# Look at all the advisories this collection is associated with
advisories = collection.update_record.all()
# Skip the first adviroy found; for any that remain, disconnect and clone
for advisory in advisories[1:]:
do_clone(apps, advisory, collection)

class Migration(migrations.Migration):

dependencies = [
('rpm', '0016_dist_tree_nofk'),
]

operations = [
migrations.RunPython(clone_reused_update_collections),
]

0 comments on commit 192a903

Please sign in to comment.