17
from canonical.launchpad.interfaces.lpstorm import (
17
from canonical.launchpad.interfaces.lpstorm import IMasterStore
21
18
from lp.registry.interfaces.distroseriesdifference import (
22
19
IDistroSeriesDifferenceSource,
24
from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
25
21
from lp.registry.interfaces.pocket import PackagePublishingPocket
26
from lp.registry.model.distroseries import DistroSeries
27
22
from lp.registry.model.distroseriesdifference import DistroSeriesDifference
28
23
from lp.registry.model.sourcepackagename import SourcePackageName
29
24
from lp.services.features import getFeatureFlag
43
38
FEATURE_FLAG_ENABLE_MODULE = u"soyuz.derived_series_jobs.enabled"
46
def make_metadata(sourcepackagename, parent_series=None):
41
def make_metadata(sourcepackagename):
47
42
"""Return JSON metadata for a job on `sourcepackagename`."""
48
# XXX JeroenVermeulen 2011-05-26 bug=758906: Make parent_series
49
# mandatory as part of multi-parent support.
50
if parent_series is None:
53
parent_id = parent_series.id
55
'sourcepackagename': sourcepackagename.id,
56
'parent_series': parent_id,
60
def create_job(derived_series, sourcepackagename, parent_series=None):
43
return {'sourcepackagename': sourcepackagename.id}
46
def create_job(distroseries, sourcepackagename):
61
47
"""Create a `DistroSeriesDifferenceJob` for a given source package.
63
:param derived_series: A `DistroSeries` that is assumed to be derived
49
:param distroseries: A `DistroSeries` that is assumed to be derived
65
51
:param sourcepackagename: The `SourcePackageName` whose publication
66
52
history has changed.
67
:param parent_series: A `DistroSeries` that is a parent of
68
`derived_series`. The difference is between the versions of
69
`sourcepackagename` in `parent_series` and `derived_series`.
71
# XXX JeroenVermeulen 2011-05-26 bug=758906: Make parent_series
72
# mandatory as part of multi-parent support.
73
54
job = DistributionJob(
74
distribution=derived_series.distribution, distroseries=derived_series,
55
distribution=distroseries.distribution, distroseries=distroseries,
75
56
job_type=DistributionJobType.DISTROSERIESDIFFERENCE,
76
metadata=make_metadata(sourcepackagename, parent_series))
57
metadata=make_metadata(sourcepackagename))
77
58
IMasterStore(DistributionJob).add(job)
78
59
return DistroSeriesDifferenceJob(job)
81
def find_waiting_jobs(derived_series, sourcepackagename, parent_series=None):
62
def find_waiting_jobs(distroseries, sourcepackagename):
82
63
"""Look for pending `DistroSeriesDifference` jobs on a package."""
83
64
# Look for identical pending jobs. This compares directly on
84
65
# the metadata string. It's fragile, but this is only an
85
66
# optimization. It's not actually disastrous to create
86
67
# redundant jobs occasionally.
87
68
json_metadata = DistributionJob.serializeMetadata(
88
make_metadata(sourcepackagename, parent_series))
69
make_metadata(sourcepackagename))
90
71
# Use master store because we don't like outdated information
92
73
store = IMasterStore(DistributionJob)
94
candidates = store.find(
96
77
DistributionJob.job_type ==
97
78
DistributionJobType.DISTROSERIESDIFFERENCE,
98
DistributionJob.distroseries == derived_series,
79
DistributionJob.distroseries == distroseries,
99
80
DistributionJob._json_data == json_metadata,
100
81
DistributionJob.job_id.is_in(Job.ready_jobs))
102
# XXX JeroenVermeulen 2011-05-26 bug=758906: Make parent_series
103
# mandatory as part of multi-parent support.
104
if parent_series is None:
105
return list(candidates)
109
for job in candidates
110
if job.metadata["parent_series"] == parent_series.id]
113
def may_require_job(derived_series, sourcepackagename, parent_series=None):
84
def may_require_job(distroseries, sourcepackagename):
114
85
"""Might publishing this package require a new job?
116
87
Use this to determine whether to create a new
119
90
runner some unnecessary work, but we don't expect a bit of
120
91
unnecessary work to be a big problem.
122
# XXX JeroenVermeulen 2011-05-26 bug=758906: Make parent_series
123
# mandatory as part of multi-parent support.
124
if derived_series is None:
126
dsp = getUtility(IDistroSeriesParentSet).getByDerivedSeries(
131
if parent.parent_series.distribution == derived_series.distribution:
132
# Differences within a distribution are not tracked.
134
existing_jobs = find_waiting_jobs(
135
derived_series, sourcepackagename, parent_series)
136
return len(existing_jobs) == 0
93
if distroseries is None:
95
parent_series = distroseries.parent_series
96
if parent_series is None:
98
if parent_series.distribution == distroseries.distribution:
99
# Differences within a distribution are not tracked.
101
return find_waiting_jobs(distroseries, sourcepackagename).is_empty()
139
104
def has_package(distroseries, sourcepackagename):
151
116
class_job_type = DistributionJobType.DISTROSERIESDIFFERENCE
154
def createForPackagePublication(cls, derived_series, sourcepackagename,
155
pocket, parent_series=None):
119
def createForPackagePublication(cls, distroseries, sourcepackagename,
156
121
"""See `IDistroSeriesDifferenceJobSource`."""
157
# XXX JeroenVermeulen 2011-05-26 bug=758906: Make parent_series
158
# mandatory as part of multi-parent support.
159
122
if not getFeatureFlag(FEATURE_FLAG_ENABLE_MODULE):
161
124
# -backports and -proposed are not really part of a standard
167
130
PackagePublishingPocket.PROPOSED):
170
children = list(derived_series.getDerivedSeries())
171
for relative in children + [derived_series]:
172
if may_require_job(relative, sourcepackagename, parent_series):
173
jobs.append(create_job(
174
relative, sourcepackagename, parent_series))
133
children = list(distroseries.getDerivedSeries())
134
for relative in children + [distroseries]:
135
if may_require_job(relative, sourcepackagename):
136
jobs.append(create_job(relative, sourcepackagename))
178
def getPendingJobsForDifferences(cls, derived_series,
179
distroseriesdifferences):
180
"""See `IDistroSeriesDifferenceJobSource`."""
181
jobs = IStore(DistributionJob).find(
183
DistributionJob.job_type == cls.class_job_type,
184
Job.id == DistributionJob.job_id,
185
Job._status.is_in(Job.PENDING_STATUSES),
186
DistributionJob.distroseries == derived_series)
188
# XXX JeroenVermeulen 2011-05-26 bug=758906: Check for parent
189
# series once it becomes available.
191
(dsd.source_package_name.id, dsd)
192
for dsd in distroseriesdifferences)
195
dsd = keyed_dsds.get(job.metadata["sourcepackagename"])
197
jobs_by_dsd.setdefault(dsd, []).append(cls(job))
201
140
def sourcepackagename(self):
202
141
return SourcePackageName.get(self.metadata['sourcepackagename'])
205
def derived_series(self):
206
return self.distroseries
209
def parent_series(self):
210
parent_id = self.metadata['parent_series']
212
# XXX JeroenVermeulen 2011-05-26 bug=758906: Make parent_series
213
# mandatory as part of multi-parent support.
215
return IStore(DistroSeries).get(DistroSeries, parent_id)
217
143
def passesPackagesetFilter(self):
218
144
"""Is this package of interest as far as packagesets are concerned?
221
147
missing in the derived series are only of interest if they are
222
148
in a packageset that the derived series also has.
224
derived_series = self.derived_series
225
parent_series = self.parent_series
226
# XXX JeroenVermeulen 2011-05-26 bug=758906: Make parent_series
227
# mandatory as part of multi-parent support.
228
if parent_series is None:
229
dsp = getUtility(IDistroSeriesParentSet).getByDerivedSeries(
231
parent_series = dsp[0].parent_series
233
sourcepackagename = self.sourcepackagename
234
if has_package(derived_series, sourcepackagename):
150
derived_series = self.distroseries
151
parent_series = derived_series.parent_series
152
if has_package(derived_series, self.sourcepackagename):
236
if not has_package(parent_series, sourcepackagename):
154
if not has_package(parent_series, self.sourcepackagename):
238
156
packagesetset = getUtility(IPackagesetSet)
239
157
if packagesetset.getBySeries(parent_series).is_empty():
241
159
# case for e.g. Debian. In that case, don't filter.
243
161
parent_sets = packagesetset.setsIncludingSource(
244
sourcepackagename, distroseries=parent_series)
162
self.sourcepackagename, distroseries=parent_series)
245
163
for parent_set in parent_sets:
246
164
for related_set in parent_set.relatedSets():
247
165
if related_set.distroseries == derived_series:
251
def getMatchingDSD(self):
252
"""Find an existing `DistroSeriesDifference` for this difference."""
253
spn_id = self.metadata["sourcepackagename"]
254
parent_id = self.metadata["parent_series"]
255
store = IMasterStore(DistroSeriesDifference)
256
# XXX JeroenVermeulen 2011-05-26 bug=758906: Make parent_series
257
# mandatory as part of multi-parent support.
258
if parent_id is None:
262
DistroSeriesDifference.parent_series_id == parent_id)
264
DistroSeriesDifference,
265
DistroSeriesDifference.derived_series == self.derived_series,
266
DistroSeriesDifference.source_package_name_id == spn_id,
271
170
"""See `IRunnableJob`."""
272
171
if not self.passesPackagesetFilter():
275
ds_diff = self.getMatchingDSD()
174
store = IMasterStore(DistroSeriesDifference)
175
ds_diff = store.find(
176
DistroSeriesDifference,
177
DistroSeriesDifference.derived_series == self.distroseries,
178
DistroSeriesDifference.source_package_name ==
179
self.sourcepackagename).one()
276
180
if ds_diff is None:
277
181
ds_diff = getUtility(IDistroSeriesDifferenceSource).new(
278
self.distroseries, self.sourcepackagename, self.parent_series)
182
self.distroseries, self.sourcepackagename)