@@ -51,10 +51,15 @@ class UnfurlVersionRangePipeline(VulnerableCodePipeline):
5151
5252 # Days elapsed before version range is re-unfurled
5353 reunfurl_after_days = 2
54+ impacted_packages = None
5455
5556 @classmethod
5657 def steps (cls ):
57- return (cls .unfurl_version_range , cls .mark_all_impacts_unfurled )
58+ return (
59+ cls .unfurl_version_range ,
60+ cls .mark_all_impacts_unfurled_sucessfully ,
61+ cls .mark_all_impacts_unfurl_attempted ,
62+ )
5863
5964 def unfurl_version_range (self ):
6065 processed_impacted_packages_count = 0
@@ -66,6 +71,7 @@ def unfurl_version_range(self):
6671 chunk_size = 500
6772
6873 impacted_packages = impacted_package_qs (cutoff_day = self .reunfurl_after_days )
74+ self .impacted_packages = impacted_packages
6975 impacted_packages_count = impacted_packages .count ()
7076 self .log (f"Unfurl affected vers range for { impacted_packages_count :,d} ImpactedPackage." )
7177
@@ -104,32 +110,67 @@ def unfurl_version_range(self):
104110 processed_impacted_packages_count += 1
105111
106112 if len (update_unfurl_date ) > update_batch_size :
113+ cur_time = timezone .now ()
107114 ImpactedPackage .objects .filter (pk__in = update_unfurl_date ).update (
108- last_range_unfurl_at = timezone . now ()
115+ last_range_unfurl_at = cur_time
109116 )
110117 ImpactedPackage .objects .filter (pk__in = update_successful_unfurl_date ).update (
111- last_successful_range_unfurl_at = timezone . now ()
118+ last_successful_range_unfurl_at = cur_time
112119 )
113120 update_unfurl_date .clear ()
114121 update_successful_unfurl_date .clear ()
115122
123+ cur_time = timezone .now ()
124+
116125 ImpactedPackage .objects .filter (pk__in = update_unfurl_date ).update (
117- last_range_unfurl_at = timezone . now ()
126+ last_range_unfurl_at = cur_time
118127 )
119128 ImpactedPackage .objects .filter (pk__in = update_successful_unfurl_date ).update (
120- last_successful_range_unfurl_at = timezone . now ()
129+ last_successful_range_unfurl_at = cur_time
121130 )
122131 self .log (f"Successfully processed { processed_impacted_packages_count :,d} ImpactedPackage." )
123132 self .log (f"{ processed_affected_packages_count :,d} new Impact-Package relation created." )
124133
125- def mark_all_impacts_unfurled (self ):
134+ def mark_all_impacts_unfurled_sucessfully (self ):
135+ impacted_packages = self .impacted_packages or impacted_package_qs (
136+ cutoff_day = self .reunfurl_after_days
137+ )
126138 while True :
127- advisories = list (latest_advisories_with_all_impacts_unfurled ()[:100 ])
139+ advisory_ids = list (
140+ latest_advisories_with_all_impacts_unfurled_successfully (
141+ impacted_packages = impacted_packages
142+ )[:100 ]
143+ )
128144
129- if not advisories :
145+ if not advisory_ids :
130146 break
131147
132- complete_advisories_import (AdvisoryV2 .objects .filter (id__in = [a .id for a in advisories ]))
148+ complete_advisories_import (advisory_ids = advisory_ids , success = True )
149+
150+ def mark_all_impacts_unfurl_attempted (self ):
151+ impacted_packages = self .impacted_packages or impacted_package_qs (
152+ cutoff_day = self .reunfurl_after_days
153+ )
154+ advisories_qs = latest_advisories_with_all_impacts_unfurled_attempted (
155+ impacted_packages = impacted_packages
156+ )
157+
158+ batch_size = 100
159+ batch = []
160+
161+ for advisory_id in advisories_qs .iterator (chunk_size = 100 ):
162+ batch .append (advisory_id )
163+
164+ if len (batch ) >= batch_size :
165+ complete_advisories_import (
166+ advisory_ids = list (batch ),
167+ )
168+ batch .clear ()
169+
170+ if batch :
171+ complete_advisories_import (
172+ advisory_ids = list (batch ),
173+ )
133174
134175
135176def get_affected_purls (versions , impact , logger ):
@@ -219,15 +260,17 @@ def impacted_package_qs(cutoff_day=2):
219260
220261
221262@transaction .atomic
222- def complete_advisories_import (advisories ):
223-
224- advisory_ids = list (advisories .values_list ("id" , flat = True ))
225-
263+ def complete_advisories_import (advisory_ids , success = False ):
226264 if not advisory_ids :
227265 return
228266
229267 AdvisoryV2 .objects .filter (id__in = advisory_ids ).update (_all_impacts_unfurled = True )
230268
269+ if success :
270+ AdvisoryV2 .objects .filter (id__in = advisory_ids ).update (
271+ _all_impacts_unfurled_successfully = True
272+ )
273+
231274 affecting_package_ids = set (
232275 ImpactedPackageAffecting .objects .filter (
233276 impacted_package__advisory_id__in = advisory_ids
@@ -256,16 +299,42 @@ def complete_advisories_import(advisories):
256299 group_advisory_for_package (package )
257300
258301
259- def latest_advisories_with_all_impacts_unfurled ():
260- remaining_unfurled_impacts = ImpactedPackage .objects .filter (
302+ def latest_advisories_with_all_impacts_unfurled_successfully (
303+ impacted_packages = None ,
304+ ):
305+ unsuccessful_impacts = impacted_packages .filter (
306+ advisory_id = OuterRef ("pk" ),
307+ advisory__is_latest = True ,
308+ ).filter (Q (last_range_unfurl_at__isnull = True ) | Q (last_successful_range_unfurl_at__isnull = True ))
309+
310+ return (
311+ AdvisoryV2 .objects .filter (
312+ _all_impacts_unfurled_successfully = False ,
313+ is_latest = True ,
314+ )
315+ .annotate (has_unsuccessful_impacts = Exists (unsuccessful_impacts ))
316+ .filter (has_unsuccessful_impacts = False )
317+ .order_by ("id" )
318+ .values_list ("id" , flat = True )
319+ )
320+
321+
322+ def latest_advisories_with_all_impacts_unfurled_attempted (
323+ impacted_packages = None ,
324+ ):
325+ impacts_not_attempted = impacted_packages .filter (
261326 advisory_id = OuterRef ("pk" ),
262- last_range_unfurl_at__isnull = True ,
263327 advisory__is_latest = True ,
328+ last_range_unfurl_at__isnull = True ,
264329 )
265330
266331 return (
267- AdvisoryV2 .objects .filter (_all_impacts_unfurled = False , is_latest = True )
268- .annotate (has_remaining_unfurled = Exists (remaining_unfurled_impacts ))
269- .filter (has_remaining_unfurled = False )
332+ AdvisoryV2 .objects .filter (
333+ _all_impacts_unfurled_successfully = False ,
334+ is_latest = True ,
335+ )
336+ .annotate (has_unattempted_impacts = Exists (impacts_not_attempted ))
337+ .filter (has_unattempted_impacts = False )
270338 .order_by ("id" )
339+ .values_list ("id" , flat = True )
271340 )
0 commit comments