launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #32990
[Merge] ~ines-almeida/launchpad:update-duplicate-vulnerability-job-logic into launchpad:master
Ines Almeida has proposed merging ~ines-almeida/launchpad:update-duplicate-vulnerability-job-logic into launchpad:master.
Commit message:
Update logic around duplicate VulnerabilityJobs
Updated the logic so that one can re-schedule a job if the previous one was superseded, or the previous one was scheduled more than 6 hours ago
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~ines-almeida/launchpad/+git/launchpad/+merge/492931
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~ines-almeida/launchpad:update-duplicate-vulnerability-job-logic into launchpad:master.
diff --git a/lib/lp/bugs/model/exportvulnerabilityjob.py b/lib/lp/bugs/model/exportvulnerabilityjob.py
index 487ca57..37a71f4 100644
--- a/lib/lp/bugs/model/exportvulnerabilityjob.py
+++ b/lib/lp/bugs/model/exportvulnerabilityjob.py
@@ -10,7 +10,7 @@ import logging
import os
import tempfile
import zipfile
-from datetime import timedelta
+from datetime import datetime, timedelta, timezone
from typing import List, Optional, Tuple
from zope.component import getUtility
@@ -92,9 +92,10 @@ class ExportVulnerabilityJob(VulnerabilityJobDerived):
VulnerabilityJob.job_id == Job.id,
VulnerabilityJob.job_type == cls.class_job_type,
VulnerabilityJob.handler == handler,
- Job._status.is_in(
- (JobStatus.WAITING, JobStatus.RUNNING, JobStatus.SUSPENDED)
- ),
+ Job._status.is_in((JobStatus.WAITING, JobStatus.RUNNING)),
+ # If the job was scheduled more than 4 hours ago (twice the soft
+ # time limit), we allow a new one to be created.
+ Job.date_created > datetime.now(timezone.utc) - timedelta(hours=4),
).one()
if vulnerability_job is not None:
diff --git a/lib/lp/bugs/model/importvulnerabilityjob.py b/lib/lp/bugs/model/importvulnerabilityjob.py
index 951e8fc..ba89aaf 100644
--- a/lib/lp/bugs/model/importvulnerabilityjob.py
+++ b/lib/lp/bugs/model/importvulnerabilityjob.py
@@ -7,7 +7,7 @@ __all__ = [
import logging
import re
-from datetime import timedelta
+from datetime import datetime, timedelta, timezone
from zope.component import getUtility
from zope.interface import implementer, provider
@@ -117,9 +117,10 @@ class ImportVulnerabilityJob(VulnerabilityJobDerived):
VulnerabilityJob.job_id == Job.id,
VulnerabilityJob.job_type == cls.class_job_type,
VulnerabilityJob.handler == handler,
- Job._status.is_in(
- (JobStatus.WAITING, JobStatus.RUNNING, JobStatus.SUSPENDED)
- ),
+ Job._status.is_in((JobStatus.WAITING, JobStatus.RUNNING)),
+ # If the job was scheduled more than 4 hours ago (twice the soft
+ # time limit), we allow a new one to be created.
+ Job.date_created > datetime.now(timezone.utc) - timedelta(hours=4),
).one()
if vulnerability_job is not None:
diff --git a/lib/lp/bugs/model/tests/test_vulnerability.py b/lib/lp/bugs/model/tests/test_vulnerability.py
index bae25c1..853194f 100644
--- a/lib/lp/bugs/model/tests/test_vulnerability.py
+++ b/lib/lp/bugs/model/tests/test_vulnerability.py
@@ -2,6 +2,7 @@
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Tests for the vulnerability and related models."""
+from datetime import timedelta
from pathlib import Path
from fixtures import MockPatch
@@ -982,6 +983,97 @@ class TestVulnerabilitySetImportData(TestCaseWithFactory):
import_since_commit_sha1=None,
)
+ def test_importData_duplicated_suspended(self):
+ """Test that we can create a duplicated ImportVulnerabilityJob
+ if the existing job is suspended for the same handler.
+ """
+ self.useContext(feature_flags())
+ set_feature_flag(VULNERABILITY_IMPORT_ENABLED_FEATURE_FLAG, "true")
+
+ self.factory.makeDistribution(name="soss", owner=self.requester)
+
+ repo = self.factory.makeGitRepository(
+ owner=self.team, information_type=InformationType.USERDATA
+ )
+
+ self.factory.makeGitRefs(
+ repository=repo,
+ paths=[self.git_ref],
+ )
+
+ vulnerability_set = getUtility(IVulnerabilitySet)
+ with person_logged_in(self.requester):
+ vulnerability_set.importData(
+ self.requester,
+ self.handler,
+ repo,
+ self.git_ref,
+ self.git_paths,
+ self.information_type,
+ import_since_commit_sha1=None,
+ )
+
+ import_job = getUtility(IImportVulnerabilityJobSource).get(
+ self.handler
+ )
+ import_job.job.suspend()
+
+ with person_logged_in(self.requester):
+ vulnerability_set.importData(
+ self.requester,
+ self.handler,
+ repo,
+ self.git_ref,
+ self.git_paths,
+ self.information_type,
+ import_since_commit_sha1=None,
+ )
+
+ def test_importData_duplicated_old_job(self):
+ """Test that we can create a a new ImportVulnerabilityJob if there is
+ a peding one for the same handler but it is older than 4 hours.
+ """
+ self.useContext(feature_flags())
+ set_feature_flag(VULNERABILITY_IMPORT_ENABLED_FEATURE_FLAG, "true")
+
+ self.factory.makeDistribution(name="soss", owner=self.requester)
+
+ repo = self.factory.makeGitRepository(
+ owner=self.team, information_type=InformationType.USERDATA
+ )
+
+ self.factory.makeGitRefs(
+ repository=repo,
+ paths=[self.git_ref],
+ )
+
+ vulnerability_set = getUtility(IVulnerabilitySet)
+ with person_logged_in(self.requester):
+ vulnerability_set.importData(
+ self.requester,
+ self.handler,
+ repo,
+ self.git_ref,
+ self.git_paths,
+ self.information_type,
+ import_since_commit_sha1=None,
+ )
+ import_job = getUtility(IImportVulnerabilityJobSource).get(
+ self.handler
+ )
+ removeSecurityProxy(import_job.job).date_created -= timedelta(hours=5)
+
+ with person_logged_in(self.requester):
+ vulnerability_set.importData(
+ self.requester,
+ self.handler,
+ repo,
+ self.git_ref,
+ self.git_paths,
+ self.information_type,
+ import_since_commit_sha1=None,
+ )
+
class TestVulnerabilitySetExportData(TestCaseWithFactory):
layer = DatabaseFunctionalLayer
@@ -1069,6 +1161,64 @@ class TestVulnerabilitySetExportData(TestCaseWithFactory):
None,
)
+ def test_exportData_duplicated_suspended(self):
+ """Test that we can create a new ExportVulnerabilityJob if there is
+ a suspended one for the same handler.
+ """
+ self.useContext(feature_flags())
+ set_feature_flag(VULNERABILITY_IMPORT_ENABLED_FEATURE_FLAG, "true")
+
+ self.factory.makeDistribution(name="soss", owner=self.requester)
+
+ vulnerability_set = getUtility(IVulnerabilitySet)
+ with person_logged_in(self.requester):
+ vulnerability_set.exportData(
+ self.requester,
+ self.handler,
+ None,
+ )
+
+ export_job = getUtility(IExportVulnerabilityJobSource).get(
+ self.handler
+ )
+ export_job.job.suspend()
+
+ with person_logged_in(self.requester):
+ vulnerability_set.exportData(
+ self.requester,
+ self.handler,
+ None,
+ )
+
+ def test_exportData_duplicated_old_job(self):
+ """Test that we can create a new ExportVulnerabilityJob if there is
+ a pending job for the same handler that is more than 4 hours old
+ """
+ self.useContext(feature_flags())
+ set_feature_flag(VULNERABILITY_IMPORT_ENABLED_FEATURE_FLAG, "true")
+
+ self.factory.makeDistribution(name="soss", owner=self.requester)
+
+ vulnerability_set = getUtility(IVulnerabilitySet)
+ with person_logged_in(self.requester):
+ vulnerability_set.exportData(
+ self.requester,
+ self.handler,
+ None,
+ )
+
+ export_job = getUtility(IExportVulnerabilityJobSource).get(
+ self.handler
+ )
+ removeSecurityProxy(export_job.job).date_created -= timedelta(hours=5)
+
+ with person_logged_in(self.requester):
+ vulnerability_set.exportData(
+ self.requester,
+ self.handler,
+ None,
+ )
+
class TestVulnerabilitySetWebService(TestCaseWithFactory):
layer = DatabaseFunctionalLayer
diff --git a/lib/lp/bugs/tests/test_exportvulnerabilityjob.py b/lib/lp/bugs/tests/test_exportvulnerabilityjob.py
index 4a83e6f..b0b6dec 100644
--- a/lib/lp/bugs/tests/test_exportvulnerabilityjob.py
+++ b/lib/lp/bugs/tests/test_exportvulnerabilityjob.py
@@ -141,6 +141,22 @@ class ExportVulnerabilityJobTests(TestCaseWithFactory):
job_duplicated.complete()
self.assertEqual(job_duplicated.status, JobStatus.COMPLETED)
+ def test_create_with_existing_suspended_job(self):
+ """If there's a suspended ExportVulnerabilityJob for the handler the
+ job can be runned again.
+ """
+ handler = VulnerabilityHandlerEnum.SOSS
+
+ job = self.job_source.create(handler)
+ job.start()
+ job.suspend()
+ self.assertEqual(job.status, JobStatus.SUSPENDED)
+
+ job_duplicated = self.job_source.create(handler)
+ job_duplicated.start()
+ job_duplicated.complete()
+ self.assertEqual(job_duplicated.status, JobStatus.COMPLETED)
+
def test_arguments(self):
"""Test that ExportVulnerabilityJob specified with arguments can
be gotten out again."""
diff --git a/lib/lp/bugs/tests/test_importvulnerabilityjob.py b/lib/lp/bugs/tests/test_importvulnerabilityjob.py
index 6ac9f77..6624f31 100644
--- a/lib/lp/bugs/tests/test_importvulnerabilityjob.py
+++ b/lib/lp/bugs/tests/test_importvulnerabilityjob.py
@@ -204,20 +204,6 @@ class ImportVulnerabilityJobTests(TestCaseWithFactory):
)
self.assertEqual(job, running_exception.job)
- # Job status from RUNNING to SUSPENDED
- job.suspend()
- running_exception = self.assertRaises(
- VulnerabilityJobInProgress,
- self.job_source.create,
- handler,
- git_repository,
- git_ref,
- git_paths,
- information_type,
- import_since_commit_sha1,
- )
- self.assertEqual(job, running_exception.job)
-
def test_create_with_existing_completed_job(self):
"""If there's already a completed ImportVulnerabilityJob for the
handler the job can be run again.
@@ -302,6 +288,41 @@ class ImportVulnerabilityJobTests(TestCaseWithFactory):
job_duplicated.complete()
self.assertEqual(job_duplicated.status, JobStatus.COMPLETED)
+ def test_create_with_existing_suspended_job(self):
+ """If there's a suspended ImportVulnerabilityJob for the handler the
+ job can be run again.
+ """
+ handler = VulnerabilityHandlerEnum.SOSS
+ git_repository = self.repository.id
+ git_ref = "ref/heads/main"
+ git_paths = ["cves"]
+ information_type = InformationType.PRIVATESECURITY.value
+ import_since_commit_sha1 = None
+
+ job = self.job_source.create(
+ handler,
+ git_repository,
+ git_ref,
+ git_paths,
+ information_type,
+ import_since_commit_sha1,
+ )
+ job.start()
+ job.suspend()
+ self.assertEqual(job.status, JobStatus.SUSPENDED)
+
+ job_duplicated = self.job_source.create(
+ handler,
+ git_repository,
+ git_ref,
+ git_paths,
+ information_type,
+ import_since_commit_sha1,
+ )
+ job_duplicated.start()
+ job_duplicated.complete()
+ self.assertEqual(job_duplicated.status, JobStatus.COMPLETED)
+
def test_arguments(self):
"""Test that ImportVulnerabilityJob specified with arguments can
be gotten out again."""
Follow ups