Merge lp:~apw/ubuntu-archive-tools/use-kernel-series-routing-information into lp:ubuntu-archive-tools

Proposed by Andy Whitcroft
Status: Merged
Approved by: Łukasz Zemczak
Approved revision: 1241
Merged at revision: 1250
Proposed branch: lp:~apw/ubuntu-archive-tools/use-kernel-series-routing-information
Merge into: lp:ubuntu-archive-tools
Diff against target: 1453 lines (+999/-266)
3 files modified
copy-proposed-kernel (+281/-258)
kernel_series.py (+657/-0)
sru-release (+61/-8)
To merge this branch: bzr merge lp:~apw/ubuntu-archive-tools/use-kernel-series-routing-information
Reviewer Review Type Date Requested Status
Łukasz Zemczak Approve
Review via email: mp+373005@code.launchpad.net

Commit message

Initial support for kernel routing lookups via KernelSeries data from the kernel team.

To post a comment you must log in.
Revision history for this message
Łukasz Zemczak (sil2100) wrote :

Ok, this generally looks good. The design of this module feels a bit strange, but I guess the reason for that is how our current tools were created. Like, it would be probably a bit more intuitive for me if KernelSeries had some get_routing_for_package() function that would simply take series and package as arguments and spit out some routing info class.
Anyway, this here is good as is, and we can certainly enhance this even further if needed.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'copy-proposed-kernel'
--- copy-proposed-kernel 2019-08-27 11:32:43 +0000
+++ copy-proposed-kernel 2019-09-19 13:51:06 +0000
@@ -32,12 +32,16 @@
3232
33from launchpadlib.launchpad import Launchpad33from launchpadlib.launchpad import Launchpad
3434
35from kernel_series import KernelSeries
36
3537
36class TestBase(unittest.TestCase):38class TestBase(unittest.TestCase):
37 class FakeArgs:39 class FakeArgs:
38 def __init__(self, **kwargs):40 def __init__(self, **kwargs):
41 self.testing = True
39 self.series = None42 self.series = None
40 self.source = None43 self.source = None
44 self.ppa2 = False
41 self.security = False45 self.security = False
42 self.security2 = False46 self.security2 = False
43 self.esm = False47 self.esm = False
@@ -45,6 +49,7 @@
45 self.ibmgt = False49 self.ibmgt = False
46 self.to_signing = False50 self.to_signing = False
47 self.from_signing = False51 self.from_signing = False
52 self.no_auto = False
4853
49 self.update(**kwargs)54 self.update(**kwargs)
5055
@@ -63,56 +68,139 @@
63 finally:68 finally:
64 sys.stdout, sys.stderr = old_out, old_err69 sys.stdout, sys.stderr = old_out, old_err
6570
71 @classmethod
72 def setUpClass(cls):
73 data = """
74 defaults:
75 routing-table:
76 default:
77 security-build:
78 - ['ppa:canonical-kernel-security-team/ubuntu/ppa', 'Release' ]
79 - ['ppa:canonical-kernel-security-team/ubuntu/ppa2', 'Release' ]
80 build:
81 - ['ppa:canonical-kernel-team/ubuntu/ppa', 'Release' ]
82 proposed:
83 - ['ubuntu', 'Proposed' ]
84 esm:
85 security-build:
86 - ['ppa:canonical-kernel-security-team/ubuntu/esm', 'Release']
87 build:
88 - ['ppa:canonical-kernel-esm/ubuntu/ppa', 'Release']
89 signing:
90 - ['ppa:canonical-signing/ubuntu/esm', 'Release']
91 proposed:
92 - ['ppa:canonical-kernel-esm/ubuntu/proposed', 'Release']
93 14.04:
94 codename: trusty
95 supported: true
96 esm: true
97 sources:
98 linux:
99 packages:
100 linux:
101 linux-signed:
102 type: signed
103 linux-meta:
104 type: meta
105 16.04:
106 codename: xenial
107 supported: true
108 sources:
109 linux-fips:
110 routing:
111 security-build:
112 - ['ppa:canonical-kernel-security-team/ubuntu/ppa', 'Release']
113 - ['ppa:canonical-kernel-security-team/ubuntu/ppa2', 'Release']
114 build:
115 - ['ppa:fips-cc-stig/ubuntu/fips-build', 'Release']
116 signing:
117 - ['ppa:canonical-signing/ubuntu/fips', 'Release']
118 proposed:
119 - ['ppa:ubuntu-advantage/ubuntu/fips-proposed', 'Release']
120 packages:
121 linux-fips:
122 linux-meta-fips:
123 type: meta
124 linux-signed-fips:
125 type: signed
126 18.04:
127 codename: bionic
128 supported: true
129 sources:
130 linux:
131 packages:
132 linux:
133 linux-signed:
134 type: signed
135 linux-meta:
136 type: meta
137 linux-ibm-gt:
138 routing:
139 security-build:
140 - ['ppa:canonical-kernel-security-team/ubuntu/ppa', 'Release']
141 - ['ppa:canonical-kernel-security-team/ubuntu/ppa2', 'Release']
142 build:
143 - ['ppa:ibm-cloud/ubuntu/build', 'Release']
144 proposed:
145 - ['ppa:ibm-cloud/ubuntu/proposed', 'Release']
146 packages:
147 linux-ibm-gt:
148 linux-meta-ibm-gt:
149 type: meta
150 """
151 cls.ks = KernelSeries(data=data)
152
66153
67class TestRouting(TestBase):154class TestRouting(TestBase):
68 def test_default(self):155 def test_default(self):
69 expected = ('~canonical-kernel-team/ubuntu/ppa', 'ubuntu', False)156 expected = (['ppa:canonical-kernel-team/ubuntu/ppa', 'Release'], ['ubuntu', 'Proposed'], False)
70 result = routing(self.FakeArgs())157 result = routing(self.FakeArgs(series='bionic', source='linux'), self.ks)
71 self.assertEqual(expected, result)158 self.assertEqual(expected, result)
72159
73 def test_security(self):160 def test_security(self):
74 expected = ('~canonical-kernel-security-team/ubuntu/ppa', 'ubuntu', True)161 expected = (['ppa:canonical-kernel-security-team/ubuntu/ppa', 'Release'], ['ubuntu', 'Proposed'], True)
75 result = routing(self.FakeArgs(security=True))162 result = routing(self.FakeArgs(series='bionic', source='linux', security=True), self.ks)
76 self.assertEqual(expected, result)163 self.assertEqual(expected, result)
77164
78 def test_security2(self):165 def test_security2(self):
79 expected = ('~canonical-kernel-security-team/ubuntu/ppa2', 'ubuntu', True)166 expected = (['ppa:canonical-kernel-security-team/ubuntu/ppa2', 'Release'], ['ubuntu', 'Proposed'], True)
80 result = routing(self.FakeArgs(security2=True))167 result = routing(self.FakeArgs(series='bionic', source='linux', security2=True), self.ks)
81 self.assertEqual(expected, result)168 self.assertEqual(expected, result)
82169
83 def test_to_signing(self):170 def test_to_signing(self):
84 expected = ('~canonical-kernel-team/ubuntu/ppa', None, False)171 expected = (['ppa:canonical-kernel-team/ubuntu/ppa', 'Release'], None, False)
85 result = routing(self.FakeArgs(to_signing=True))172 result = routing(self.FakeArgs(series='bionic', source='linux', to_signing=True), self.ks)
86 self.assertEqual(expected, result)173 self.assertEqual(expected, result)
87174
88 def test_from_signing(self):175 def test_from_signing(self):
89 expected = (None, 'ubuntu', False)176 expected = (None, ['ubuntu', 'Proposed'], False)
90 result = routing(self.FakeArgs(from_signing=True))177 result = routing(self.FakeArgs(series='bionic', source='linux', from_signing=True), self.ks)
91 self.assertEqual(expected, result)178 self.assertEqual(expected, result)
92179
93 def test_esm(self):180 def test_esm(self):
94 expected = ('~canonical-kernel-esm/ubuntu/ppa', '~canonical-kernel-esm/ubuntu/proposed', False)181 expected = (['ppa:canonical-kernel-esm/ubuntu/ppa', 'Release'], ['ppa:canonical-signing/ubuntu/esm', 'Release'], False)
95 result = routing(self.FakeArgs(esm=True))182 result = routing(self.FakeArgs(series='trusty', source='linux'), self.ks)
96 self.assertEqual(expected, result)183 self.assertEqual(expected, result)
97184
98 def test_esm_security(self):185 def test_esm_security(self):
99 expected = ('~canonical-kernel-security-team/ubuntu/esm', '~canonical-kernel-esm/ubuntu/proposed', False)186 expected = (['ppa:canonical-kernel-security-team/ubuntu/esm', 'Release'], ['ppa:canonical-signing/ubuntu/esm', 'Release'], False)
100 result = routing(self.FakeArgs(esm=True, security=True))187 result = routing(self.FakeArgs(series='trusty', source='linux', security=True), self.ks)
101 self.assertEqual(expected, result)188 self.assertEqual(expected, result)
102189
103 def test_esm_security2(self):190 def test_esm_security2(self):
104 expected = (None, '~canonical-kernel-esm/ubuntu/proposed', False)191 with self.assertRaises(SystemExit), self.capture() as (out, err):
105 result = routing(self.FakeArgs(esm=True, security2=True))192 expected = (None, ['ppa:canonical-kernel-esm/ubuntu/proposed', 'Release'], False)
106 self.assertEqual(expected, result)193 result = routing(self.FakeArgs(series='trusty', source='linux', security2=True), self.ks)
194 self.assertEqual(expected, result)
107195
108 def test_esm_to_signing(self):196 def test_esm_to_signing(self):
109 expected = ('~canonical-kernel-esm/ubuntu/ppa', '~canonical-signing/ubuntu/esm', False)197 expected = (['ppa:canonical-kernel-esm/ubuntu/ppa', 'Release'], ['ppa:canonical-signing/ubuntu/esm', 'Release'], False)
110 result = routing(self.FakeArgs(esm=True, to_signing=True))198 result = routing(self.FakeArgs(series='trusty', source='linux', esm=True, to_signing=True), self.ks)
111 self.assertEqual(expected, result)199 self.assertEqual(expected, result)
112200
113 def test_esm_from_signing(self):201 def test_esm_from_signing(self):
114 expected = ('~canonical-signing/ubuntu/esm', '~canonical-kernel-esm/ubuntu/proposed', False)202 expected = (['ppa:canonical-signing/ubuntu/esm', 'Release'], ['ppa:canonical-kernel-esm/ubuntu/proposed', 'Release'], False)
115 result = routing(self.FakeArgs(esm=True, from_signing=True))203 result = routing(self.FakeArgs(series='trusty', source='linux', esm=True, from_signing=True), self.ks)
116 self.assertEqual(expected, result)204 self.assertEqual(expected, result)
117205
118 # Autorouting will enable to_signing, the user will then want to switch us206 # Autorouting will enable to_signing, the user will then want to switch us
@@ -120,226 +208,145 @@
120 # simple we make from_signing take presidence over to_signing. Test this208 # simple we make from_signing take presidence over to_signing. Test this
121 # is honoured correctly.209 # is honoured correctly.
122 def test_esm_from_signing_override_to_signing(self):210 def test_esm_from_signing_override_to_signing(self):
123 expected = ('~canonical-signing/ubuntu/esm', '~canonical-kernel-esm/ubuntu/proposed', False)211 expected = (['ppa:canonical-signing/ubuntu/esm', 'Release'], ['ppa:canonical-kernel-esm/ubuntu/proposed', 'Release'], False)
124 result = routing(self.FakeArgs(esm=True, to_signing=True, from_signing=True))212 result = routing(self.FakeArgs(series='trusty', source='linux', esm=True, to_signing=True, from_signing=True), self.ks)
125 self.assertEqual(expected, result)213 self.assertEqual(expected, result)
126214
127 def test_fips(self):215 def test_fips(self):
128 expected = ('~fips-cc-stig/ubuntu/fips-build', '~ubuntu-advantage/ubuntu/fips-proposed', False)216 expected = (['ppa:fips-cc-stig/ubuntu/fips-build', 'Release'], ['ppa:canonical-signing/ubuntu/fips', 'Release'], False)
129 result = routing(self.FakeArgs(fips=True))217 result = routing(self.FakeArgs(series='xenial', source='linux-fips'), self.ks)
130 self.assertEqual(expected, result)218 self.assertEqual(expected, result)
131219
132 def test_fips_security(self):220 def test_fips_security(self):
133 expected = ('~canonical-kernel-security-team/ubuntu/ppa', '~ubuntu-advantage/ubuntu/fips-proposed', False)221 expected = (['ppa:canonical-kernel-security-team/ubuntu/ppa', 'Release'], ['ppa:canonical-signing/ubuntu/fips', 'Release'], False)
134 result = routing(self.FakeArgs(fips=True, security=True))222 result = routing(self.FakeArgs(series='xenial', source='linux-fips', security=True), self.ks)
135 self.assertEqual(expected, result)223 self.assertEqual(expected, result)
136224
137 def test_fips_security2(self):225 def test_fips_security2(self):
138 expected = ('~canonical-kernel-security-team/ubuntu/ppa2', '~ubuntu-advantage/ubuntu/fips-proposed', False)226 expected = (['ppa:canonical-kernel-security-team/ubuntu/ppa2', 'Release'], ['ppa:canonical-signing/ubuntu/fips', 'Release'], False)
139 result = routing(self.FakeArgs(fips=True, security2=True))227 result = routing(self.FakeArgs(series='xenial', source='linux-fips', security2=True), self.ks)
140 self.assertEqual(expected, result)228 self.assertEqual(expected, result)
141229
142 def test_fips_to_signing(self):230 def test_fips_to_signing(self):
143 expected = ('~fips-cc-stig/ubuntu/fips-build', '~canonical-signing/ubuntu/fips', False)231 expected = (['ppa:fips-cc-stig/ubuntu/fips-build', 'Release'], ['ppa:canonical-signing/ubuntu/fips', 'Release'], False)
144 result = routing(self.FakeArgs(fips=True, to_signing=True))232 result = routing(self.FakeArgs(series='xenial', source='linux-fips', to_signing=True), self.ks)
145 self.assertEqual(expected, result)233 self.assertEqual(expected, result)
146234
147 def test_fips_from_signing(self):235 def test_fips_from_signing(self):
148 expected = ('~canonical-signing/ubuntu/fips', '~ubuntu-advantage/ubuntu/fips-proposed', False)236 expected = (['ppa:canonical-signing/ubuntu/fips', 'Release'], ['ppa:ubuntu-advantage/ubuntu/fips-proposed', 'Release'], False)
149 result = routing(self.FakeArgs(fips=True, from_signing=True))237 result = routing(self.FakeArgs(series='xenial', source='linux-fips', from_signing=True), self.ks)
150 self.assertEqual(expected, result)238 self.assertEqual(expected, result)
151239
152 def test_ibmgt(self):240 def test_ibmgt(self):
153 expected = ('~ibm-cloud/ubuntu/build', '~ibm-cloud/ubuntu/proposed', False)241 expected = (['ppa:ibm-cloud/ubuntu/build', 'Release'], ['ppa:ibm-cloud/ubuntu/proposed', 'Release'], False)
154 result = routing(self.FakeArgs(ibmgt=True))242 result = routing(self.FakeArgs(series='bionic', source='linux-ibm-gt'), self.ks)
155 self.assertEqual(expected, result)243 self.assertEqual(expected, result)
156244
157 def test_ibmgt_security(self):245 def test_ibmgt_security(self):
158 expected = ('~canonical-kernel-security-team/ubuntu/ppa', '~ibm-cloud/ubuntu/proposed', False)246 expected = (['ppa:canonical-kernel-security-team/ubuntu/ppa', 'Release'], ['ppa:ibm-cloud/ubuntu/proposed', 'Release'], False)
159 result = routing(self.FakeArgs(ibmgt=True, security=True))247 result = routing(self.FakeArgs(series='bionic', source='linux-ibm-gt', security=True), self.ks)
160 self.assertEqual(expected, result)248 self.assertEqual(expected, result)
161249
162 def test_ibmgt_security2(self):250 def test_ibmgt_security2(self):
163 expected = ('~canonical-kernel-security-team/ubuntu/ppa2', '~ibm-cloud/ubuntu/proposed', False)251 expected = (['ppa:canonical-kernel-security-team/ubuntu/ppa2', 'Release'], ['ppa:ibm-cloud/ubuntu/proposed', 'Release'], False)
164 result = routing(self.FakeArgs(ibmgt=True, security2=True))252 result = routing(self.FakeArgs(series='bionic', source='linux-ibm-gt', security2=True), self.ks)
165 self.assertEqual(expected, result)253 self.assertEqual(expected, result)
166254
167255
168def routing(args):256def routing(args, ks):
257 series_name = args.series
258 package_name = args.source
259
260 series = ks.lookup_series(codename=series_name)
261 if series is None:
262 print("ERROR: {} -- series unknown".format(series_name))
263 sys.exit(1)
264
265 package = None
266 package_signed = None
267 for source_srch in series.sources:
268 package_signed = None
269 for package_srch in source_srch.packages:
270 if package_srch.name == package_name:
271 package = package_srch
272 if (package_srch.name.startswith('linux-signed-') or
273 package_srch.name == 'linux-signed'):
274 package_signed = package_srch
275 if package is not None:
276 break
277 if package is None:
278 print("ERROR: {}/{} -- package unknown".format(series_name, package_name))
279 sys.exit(1)
280
281 source = package.source
282 routing = source.routing
283 if routing is None:
284 print("ERROR: {}/{} -- package has no routing".format(series_name, package_name))
285 sys.exit(1)
286
287 build_archives = routing.lookup_destination('build')
288 security_archives = routing.lookup_destination('security-build')
289 proposed_archive = routing.lookup_destination('proposed', primary=True)
290 signing_archive = routing.lookup_destination('signing', primary=True)
291
292 if build_archives is None or len(build_archives) < 1:
293 print("ERROR: {}/{} -- package has no primary build archive".format(series_name, package_name))
294 sys.exit(1)
295 if args.ppa2 and (build_archives is None or len(build_archives) < 2):
296 print("ERROR: {}/{} -- package has no secondary build archive".format(series_name, package_name))
297 sys.exit(1)
298 if build_archives is None:
299 print("ERROR: {}/{} -- package has no build archive".format(series_name, package_name))
300 sys.exit(1)
301 if proposed_archive is None:
302 print("ERROR: {}/{} -- package has no proposed archive".format(series_name, package_name))
303 sys.exit(1)
304 if args.security and (security_archives is None or len(security_archives) < 1):
305 print("ERROR: {}/{} -- package has no primary security archive".format(series_name, package_name))
306 sys.exit(1)
307 if args.security2 and (security_archives is None or len(security_archives) < 2):
308 print("ERROR: {}/{} -- package has no secondary security archive".format(series_name, package_name))
309 sys.exit(1)
310
311 # Default route build -> proposed
312 if args.ppa2:
313 from_archive = build_archives[1]
314 else:
315 from_archive = build_archives[0]
316 to_archive = proposed_archive
317
169 unembargo = False318 unembargo = False
170319
171 # Default routing.
172 security_name = (
173 '~canonical-kernel-security-team/ubuntu/ppa',
174 '~canonical-kernel-security-team/ubuntu/ppa2',
175 )
176 signing = None
177
178 # ESM specific routing
179 if args.esm:
180 ppa_name = '~canonical-kernel-esm/ubuntu/ppa'
181 to = '~canonical-kernel-esm/ubuntu/proposed'
182 signing = '~canonical-signing/ubuntu/esm'
183 security_name = (
184 '~canonical-kernel-security-team/ubuntu/esm',
185 None,
186 )
187
188 # FIPS specific routing
189 elif args.fips:
190 ppa_name = '~fips-cc-stig/ubuntu/fips-build'
191 to = '~ubuntu-advantage/ubuntu/fips-proposed'
192 signing = '~canonical-signing/ubuntu/fips'
193
194 # IBMGT specific routing
195 elif args.ibmgt:
196 ppa_name = '~ibm-cloud/ubuntu/build'
197 to = '~ibm-cloud/ubuntu/proposed'
198
199 # Default routing.
200 else:
201 ppa_name = '~canonical-kernel-team/ubuntu/ppa'
202 to = 'ubuntu'
203 if args.security or args.security2:
204 # Allow us to unembargo when releasing from security to ubuntu.
205 unembargo = True
206
207 # Handle security routing.320 # Handle security routing.
208 if args.security:321 if args.security:
209 ppa_name = security_name[0]322 from_archive = security_archives[0]
210 elif args.security2:323 if args.security2:
211 ppa_name = security_name[1]324 from_archive = security_archives[1]
325
326 # Allow us to unembargo when releasing from security to ubuntu.
327 if (args.security or args.security2) and to_archive[0] == 'ubuntu':
328 unembargo = True
212329
213 # Handle signing routing.330 # Handle signing routing.
214 if args.from_signing:331 if args.from_signing:
215 ppa_name = signing332 from_archive = signing_archive
216 elif args.to_signing:333 elif args.to_signing:
217 to = signing334 to_archive = signing_archive
218335 # Automatically route to signing by default.
219 return (ppa_name, to, unembargo)336 elif args.no_auto is False and signing_archive is not None and package_signed is not None:
220337 to_archive = signing_archive
221class TestAutoRoute(TestBase):338
222 def test_default(self):339 # Announce the routing if needed.
223 results = self.FakeArgs(series='bionic', source='linux')340 if (args.testing is False and (routing.name != 'default' or from_archive == signing_archive or to_archive == signing_archive)):
224 expected = copy(results).update()341 msg = "NOTE: directing copy using {} routes".format(routing.name)
225 with self.capture() as (out, err):342 if from_archive == signing_archive:
226 auto_route(results)343 msg += ' from signing'
227 self.assertEqual(expected.__dict__, results.__dict__)344 elif to_archive == signing_archive:
228 self.assertEqual('', out.getvalue().strip())345 msg += ' to signing'
229
230 def test_esm_precise_linux(self):
231 results = self.FakeArgs(series='precise', source='linux')
232 expected = copy(results).update(esm=True)
233 with self.capture() as (out, err):
234 auto_route(results)
235 self.assertEqual(expected.__dict__, results.__dict__)
236 self.assertIn('from and to ESM', out.getvalue().strip())
237 self.assertNotIn('via signing', out.getvalue().strip())
238
239 def test_esm_precise_linux_meta(self):
240 results = self.FakeArgs(series='precise', source='linux-meta')
241 expected = copy(results).update(esm=True)
242 with self.capture() as (out, err):
243 auto_route(results)
244 self.assertEqual(expected.__dict__, results.__dict__)
245 self.assertIn('from and to ESM', out.getvalue().strip())
246 self.assertNotIn('via signing', out.getvalue().strip())
247
248 def test_esm_precise_lbm(self):
249 results = self.FakeArgs(series='precise', source='linux-backports-modules-3.2.0')
250 expected = copy(results).update(esm=True)
251 with self.capture() as (out, err):
252 auto_route(results)
253 self.assertEqual(expected.__dict__, results.__dict__)
254 self.assertIn('from and to ESM', out.getvalue().strip())
255 self.assertNotIn('via signing', out.getvalue().strip())
256
257 def test_esm_precise_linux_lts_trusty(self):
258 results = self.FakeArgs(series='precise', source='linux-lts-trusty')
259 expected = copy(results).update(esm=True, to_signing=True)
260 with self.capture() as (out, err):
261 auto_route(results)
262 self.assertEqual(expected.__dict__, results.__dict__)
263 self.assertIn('from and to ESM', out.getvalue().strip())
264 self.assertIn('via signing', out.getvalue().strip())
265
266 def test_esm_trusty_linux(self):
267 results = self.FakeArgs(series='trusty', source='linux')
268 expected = copy(results).update(esm=True, to_signing=True)
269 with self.capture() as (out, err):
270 auto_route(results)
271 self.assertEqual(expected.__dict__, results.__dict__)
272 self.assertIn('from and to ESM', out.getvalue().strip())
273 self.assertIn('via signing', out.getvalue().strip())
274
275 def test_esm_trusty_linux_aws(self):
276 results = self.FakeArgs(series='trusty', source='linux-aws')
277 expected = copy(results).update(esm=True)
278 with self.capture() as (out, err):
279 auto_route(results)
280 self.assertEqual(expected.__dict__, results.__dict__)
281 self.assertIn('from and to ESM', out.getvalue().strip())
282 self.assertNotIn('via signing', out.getvalue().strip())
283
284 def test_esm_precise_linux_lts_trusty(self):
285 results = self.FakeArgs(series='precise', source='linux-lts-trusty')
286 expected = copy(results).update(esm=True, to_signing=True)
287 with self.capture() as (out, err):
288 auto_route(results)
289 self.assertEqual(expected.__dict__, results.__dict__)
290 self.assertIn('from and to ESM', out.getvalue().strip())
291 self.assertIn('via signing', out.getvalue().strip())
292
293 def test_fips_bionic_linux_fips(self):
294 results = self.FakeArgs(series='bionic', source='linux-fips')
295 expected = copy(results).update(fips=True, to_signing=True)
296 with self.capture() as (out, err):
297 auto_route(results)
298 self.assertEqual(expected.__dict__, results.__dict__)
299 self.assertIn('from and to FIPS', out.getvalue().strip())
300 self.assertIn('via signing', out.getvalue().strip())
301
302 def test_ibmgt_bionic_linux_ibmgt(self):
303 results = self.FakeArgs(series='bionic', source='linux-ibm-gt')
304 expected = copy(results).update(ibmgt=True)
305 with self.capture() as (out, err):
306 auto_route(results)
307 self.assertEqual(expected.__dict__, results.__dict__)
308 self.assertIn('from and to IBM-GT', out.getvalue().strip())
309 self.assertNotIn('via signing', out.getvalue().strip())
310
311def auto_route(args):
312 before = (args.esm, args.fips, args.ibmgt, args.to_signing)
313
314 if args.series in ('precise', 'trusty'):
315 args.esm = True
316 if args.esm:
317 if (args.series == 'precise' and args.source in ('linux', 'linux-meta', 'linux-backports-modules-3.2.0') or
318 args.series == 'trusty' and args.source in ('linux-aws', 'linux-meta-aws')):
319 args.to_signing = False
320 else:
321 args.to_signing = True
322
323 if args.source.endswith('-fips'):
324 args.fips = True
325 if args.fips:
326 args.to_signing = True
327
328 if args.source.endswith('-ibm-gt'):
329 args.ibmgt = True
330
331 if before != (args.esm, args.fips, args.ibmgt, args.to_signing):
332 msg = "NOTE: directing copy "
333 if args.esm:
334 msg += "from and to ESM"
335 elif args.fips:
336 msg += "from and to FIPS"
337 elif args.ibmgt:
338 msg += "from and to IBM-GT PPAs"
339 if args.to_signing:
340 msg += " via signing"
341 print(msg)346 print(msg)
342347
348 return (from_archive, to_archive, unembargo)
349
343350
344# SELF-TESTS:351# SELF-TESTS:
345if len(sys.argv) >= 2 and sys.argv[1] == '--self-test':352if len(sys.argv) >= 2 and sys.argv[1] == '--self-test':
@@ -347,7 +354,9 @@
347 sys.exit(0)354 sys.exit(0)
348355
349parser = argparse.ArgumentParser(description='Copy a proposed kernel to the apropriate archive pocket')356parser = argparse.ArgumentParser(description='Copy a proposed kernel to the apropriate archive pocket')
357parser.set_defaults(testing=False)
350parser.add_argument('--dry-run', action='store_true', help='Do everything but actually copy the package')358parser.add_argument('--dry-run', action='store_true', help='Do everything but actually copy the package')
359parser.add_argument('--ppa2', action='store_true', help='Copy from the kernel build PPA2')
351parser.add_argument('--security', '-S', action='store_true', help='Copy from the kernel security PPA')360parser.add_argument('--security', '-S', action='store_true', help='Copy from the kernel security PPA')
352parser.add_argument('--security2', action='store_true', help='Copy from the kernel security PPA2')361parser.add_argument('--security2', action='store_true', help='Copy from the kernel security PPA2')
353parser.add_argument('--esm', '-E', action='store_true', help='Copy from the kernel ESM PPA and to the kernel ESM proposed PPA')362parser.add_argument('--esm', '-E', action='store_true', help='Copy from the kernel ESM PPA and to the kernel ESM proposed PPA')
@@ -357,73 +366,87 @@
357parser.add_argument('--to-signing', action='store_true', help='Copy from the kernel ESM/FIPS PPA to the ESM/FIPS signing PPA')366parser.add_argument('--to-signing', action='store_true', help='Copy from the kernel ESM/FIPS PPA to the ESM/FIPS signing PPA')
358parser.add_argument('--from-signing', action='store_true', help='Copy from the ESM/FIPS signing PPA to the ESM/FIPS proposed PPA')367parser.add_argument('--from-signing', action='store_true', help='Copy from the ESM/FIPS signing PPA to the ESM/FIPS proposed PPA')
359parser.add_argument('series', action='store', help='The series the source package is in')368parser.add_argument('series', action='store', help='The series the source package is in')
360parser.add_argument('source', action='store', help='The source package name')369parser.add_argument('source', action='store', nargs='+', help='The source package name')
361370
362args = parser.parse_args()371args = parser.parse_args()
363372
364# If we are allowed to intuit destinations do so:373if args.esm or args.fips or args.ibmgt:
365# 1) precise is now destined for the ESM PPAs374 print("NOTE: flags --esm, --fips, and --ibmgt are now deprecated")
366if not args.no_auto:375
367 auto_route(args)376release = args.series
368377
369(ppa_name, to, security) = routing(args)378ks = KernelSeries()
370
371##print("ppa_name<{}> to<{}>".format(ppa_name, to))
372
373if ppa_name is None:
374 print("ERROR: bad source PPA")
375 sys.exit(1)
376if to is None:
377 print("ERROR: bad destination")
378 sys.exit(1)
379
380(release, pkg) = (args.series, args.source)
381379
382launchpad = Launchpad.login_with(380launchpad = Launchpad.login_with(
383 'ubuntu-archive-tools', 'production', version='devel')381 'ubuntu-archive-tools', 'production', version='devel')
384ubuntu = launchpad.distributions['ubuntu']382ubuntu = launchpad.distributions['ubuntu']
385distro_series = ubuntu.getSeries(name_or_version=release)383distro_series = ubuntu.getSeries(name_or_version=release)
386kernel_ppa = launchpad.archives.getByReference(384
387 reference=ppa_name)385copies = []
388386for pkg in list(args.source):
389# Grab a reference to the 'to' archive and select a pocket.387 # BODGE: routing should just take release/pkg.
390to_archive = launchpad.archives.getByReference(reference=to)388 args.source = pkg
391if to == 'ubuntu':389
392 to_pocket = 'proposed'390 (from_archive, to_archive, security) = routing(args, ks)
393else:391 ##print("from_archive<{}> to_archive<{}>".format(from_archive, to_archive))
394 to_pocket = 'release'392
395393 if from_archive is None:
396# get current version in PPA for that series394 print("ERROR: bad source PPA")
397versions = kernel_ppa.getPublishedSources(395 sys.exit(1)
398 source_name=pkg, exact_match=True, status='Published', pocket='Release',396 if to_archive is None:
399 distro_series=distro_series)397 print("ERROR: bad destination")
400version = None398 sys.exit(1)
401if versions.total_size == 1:399
402 version = versions[0].source_package_version400 (from_reference, from_pocket) = from_archive
403401 (to_reference, to_pocket) = to_archive
404include_binaries = (pkg not in ('debian-installer')402
405 and not pkg.startswith('linux-signed'))403 # Grab a reference to the 'from' archive.
406if args.from_signing:404 from_archive = launchpad.archives.getByReference(
407 include_binaries = True405 reference=from_reference)
408406
409print("""Copying {}/{}:407 # Grab a reference to the 'to' archive.
410 From: {} release408 to_archive = launchpad.archives.getByReference(reference=to_reference)
411 To: {} {}409
412 Binaries: {}""".format(pkg, version, kernel_ppa, to_archive, to_pocket, include_binaries))410 # get current version in PPA for that series
413411 versions = from_archive.getPublishedSources(
414if not version:412 source_name=pkg, exact_match=True, status='Published', pocket=from_pocket,
415 print("ERROR: no version to copy")413 distro_series=distro_series)
416 sys.exit(1)414 version = None
415 if versions.total_size == 1:
416 version = versions[0].source_package_version
417
418 include_binaries = (pkg not in ('debian-installer')
419 and not pkg.startswith('linux-signed'))
420 if args.from_signing:
421 include_binaries = True
422
423 print("""Copying {}/{}:
424 From: {} {}
425 To: {} {}
426 Binaries: {}""".format(pkg, version, from_archive.reference, from_pocket, to_archive.reference, to_pocket, include_binaries))
427
428 if not version:
429 print("ERROR: no version to copy")
430 sys.exit(1)
431
432 copies.append({
433 'from_archive': from_archive,
434 'include_binaries': include_binaries,
435 'source_name': pkg,
436 'to_series': release,
437 'to_pocket': to_pocket,
438 'version': version,
439 'auto_approve': True,
440 'unembargo': security,
441 })
417442
418if args.dry_run:443if args.dry_run:
419 print("Dry run; no packages copied.")444 print("Dry run; no packages copied.")
420 sys.exit(0)445 sys.exit(0)
421446
422# Finally ready to actually copy this.447for copy in copies:
423to_archive.copyPackage(448 # We found valid packages for each requested element, actually copy them.
424 from_archive=kernel_ppa, include_binaries=include_binaries,449 to_archive.copyPackage(**copy)
425 source_name=pkg, to_series=release, to_pocket=to_pocket, version=version,
426 auto_approve=True, unembargo=security)
427450
428# TODO: adjust this script to use find-bin-overrides or rewrite451# TODO: adjust this script to use find-bin-overrides or rewrite
429# find-bin-overrides to use lpapi and use it here.452# find-bin-overrides to use lpapi and use it here.
430453
=== added file 'kernel_series.py'
--- kernel_series.py 1970-01-01 00:00:00 +0000
+++ kernel_series.py 2019-09-19 13:51:06 +0000
@@ -0,0 +1,657 @@
1#!/usr/bin/env python
2#
3
4try:
5 from urllib.request import urlopen
6except ImportError:
7 from urllib2 import urlopen
8
9import os
10import yaml
11
12class KernelRoutingEntry:
13 def __init__(self, ks, source, data):
14 name = "{}:{}".format(source.series.codename, source.name)
15 if isinstance(data, str):
16 name = data
17 table = source.series.routing_table
18 if table is None:
19 raise ValueError("unable to map routing alias {}, "
20 "no series routing table".format(data))
21 if data not in table:
22 raise ValueError("unable to map routing alias {}, "
23 "not listed in series routing table".format(data))
24 data = table[data]
25
26 # Clear out any entries that have been overriden to None.
27 for entry in dict(data):
28 if data[entry] is None:
29 del data[entry]
30
31 self._ks = ks
32 self._source = source
33 self._name = name
34 self._data = data if data else {}
35
36 @property
37 def source(self):
38 return self._source
39
40 @property
41 def name(self):
42 return self._name
43
44 def __eq__(self, other):
45 if isinstance(self, other.__class__):
46 return list(self) == list(other)
47 return False
48
49 def __ne__(self, other):
50 return not self.__eq__(other)
51
52 def __iter__(self):
53 return iter(self._data.items())
54
55 def __getitem__(self, which):
56 return self._data[which]
57
58 def lookup_destination(self, dest, primary=False):
59 data = self._data.get(dest, None)
60 if primary is False or data is None:
61 return data
62 return data[0]
63
64 def __str__(self):
65 return str(self._data)
66
67
68class KernelRepoEntry:
69 def __init__(self, ks, owner, data):
70 if isinstance(data, list):
71 new_data = {'url': data[0]}
72 if len(data) == 1:
73 new_data['branch'] = 'master'
74 elif len(data) == 2:
75 new_data['branch'] = data[1]
76 data = new_data
77
78 self._ks = ks
79 self._owner = owner
80 self._data = data if data else {}
81
82 @property
83 def owner(self):
84 return self._owner
85
86 # XXX: should this object have a name ?
87
88 def __eq__(self, other):
89 if isinstance(self, other.__class__):
90 return self.url == other.url and self.branch == other.branch
91 return False
92
93 def __ne__(self, other):
94 return not self.__eq__(other)
95
96 @property
97 def url(self):
98 return self._data['url']
99
100 @property
101 def branch(self):
102 return self._data.get('branch', None)
103
104 def __str__(self):
105 return "{} {}".format(self.url, self.branch)
106
107
108class KernelSnapEntry:
109 def __init__(self, ks, source, name, data):
110 self._ks = ks
111 self._source = source
112 self._name = name
113 self._data = data if data else {}
114
115 # Convert arches/track to publish-to form.
116 if 'publish-to' not in self._data:
117 if 'arches' in self._data:
118 publish_to = {}
119 for arch in self._data['arches']:
120 publish_to[arch] = [self._data.get('track', 'latest')]
121 self._data['publish-to'] = publish_to
122
123 # Convert stable to promote-to form.
124 if 'promote-to' not in self._data and 'stable' in self._data:
125 if self._data['stable'] is True:
126 self._data['promote-to'] = 'stable'
127 else:
128 self._data['promote-to'] = 'candidate'
129 # Assume no promote-to data to mean just to edge.
130 promote_to = self._data.get('promote-to', 'edge')
131 if isinstance(promote_to, str):
132 expand_promote_to = []
133 for risk in ('edge', 'beta', 'candidate', 'stable'):
134 expand_promote_to.append(risk)
135 if risk == promote_to:
136 break
137 self._data['promote-to'] = expand_promote_to
138 # Ensure we have stable when promote-to is present.
139 if 'promote-to' in self._data and 'stable' not in self._data:
140 if 'stable' in self._data['promote-to']:
141 self._data['stable'] = True
142 else:
143 self._data['stable'] = False
144
145 def __eq__(self, other):
146 if isinstance(self, other.__class__):
147 return self.name == other.name and self.source == other.source
148 return False
149
150 def __ne__(self, other):
151 return not self.__eq__(other)
152
153 @property
154 def series(self):
155 return self._source.series
156
157 @property
158 def source(self):
159 return self._source
160
161 @property
162 def name(self):
163 return self._name
164
165 @property
166 def repo(self):
167 data = self._data.get('repo', None)
168 if not data:
169 return None
170 return KernelRepoEntry(self._ks, self, data)
171
172 @property
173 def primary(self):
174 return self._data.get('primary', False)
175
176 @property
177 def gated(self):
178 return self._data.get('gated', False)
179
180 @property
181 def stable(self):
182 return self._data.get('stable', False)
183
184 @property
185 def qa(self):
186 return self._data.get('qa', False)
187
188 @property
189 def hw_cert(self):
190 return self._data.get('hw-cert', False)
191
192 @property
193 def arches(self):
194 # XXX: should this be []
195 return self._data.get('arches', None)
196
197 @property
198 def track(self):
199 return self._data.get('track', None)
200
201 @property
202 def publish_to(self):
203 return self._data.get('publish-to', None)
204
205 @property
206 def promote_to(self):
207 return self._data.get('promote-to', None)
208
209 def promote_to_risk(self, risk):
210 return risk in self._data.get('promote-to', [])
211
212 def __str__(self):
213 return "{} {}".format(str(self.source), self.name)
214
215
216class KernelPackageEntry:
217 def __init__(self, ks, source, name, data):
218 self._ks = ks
219 self._source = source
220 self._name = name
221 self._data = data if data else {}
222
223 def __eq__(self, other):
224 if isinstance(self, other.__class__):
225 return self.name == other.name and self.source == other.source
226 return False
227
228 def __ne__(self, other):
229 return not self.__eq__(other)
230
231 @property
232 def series(self):
233 return self._source.series
234
235 @property
236 def source(self):
237 return self._source
238
239 @property
240 def name(self):
241 return self._name
242
243 @property
244 def type(self):
245 return self._data.get('type', None)
246
247 @property
248 def repo(self):
249 data = self._data.get('repo', None)
250 if not data:
251 return None
252 return KernelRepoEntry(self._ks, self, data)
253
254 def __str__(self):
255 return "{} {} {}".format(str(self.source), self.name, self.type)
256
257
258class KernelSourceEntry:
259 def __init__(self, ks, series, name, data):
260 self._ks = ks
261 self._series = series
262 self._name = name
263 self._data = data if data else {}
264
265 def __eq__(self, other):
266 if isinstance(self, other.__class__):
267 return self.name == other.name and self.series == other.series
268 return False
269
270 def __ne__(self, other):
271 return not self.__eq__(other)
272
273 @property
274 def name(self):
275 return self._name
276
277 @property
278 def series(self):
279 return self._series
280
281 @property
282 def versions(self):
283 if 'versions' in self._data:
284 return self._data['versions']
285
286 derived_from = self.derived_from
287 if derived_from is not None:
288 return derived_from.versions
289
290 copy_forward = self.copy_forward
291 if copy_forward is not None:
292 return copy_forward.versions
293
294 # XXX: should this be []
295 return None
296
297 @property
298 def version(self):
299 versions = self.versions
300 if not versions:
301 return None
302 return versions[-1]
303
304 @property
305 def development(self):
306 return self._data.get('development', self.series.development)
307
308 @property
309 def supported(self):
310 return self._data.get('supported', self.series.supported)
311
312 @property
313 def severe_only(self):
314 return self._data.get('severe-only', False)
315
316 @property
317 def stakeholder(self):
318 return self._data.get('stakeholder', None)
319
320 @property
321 def packages(self):
322 # XXX: should this return None when empty
323 result = []
324 packages = self._data.get('packages')
325 if packages:
326 for package_key, package in packages.items():
327 result.append(KernelPackageEntry(self._ks, self, package_key, package))
328 return result
329
330 def lookup_package(self, package_key):
331 packages = self._data.get('packages')
332 if not packages or package_key not in packages:
333 return None
334 return KernelPackageEntry(self._ks, self, package_key, packages[package_key])
335
336 @property
337 def snaps(self):
338 # XXX: should this return None when empty
339 result = []
340 snaps = self._data.get('snaps')
341 if snaps:
342 for snap_key, snap in snaps.items():
343 result.append(KernelSnapEntry(self._ks, self, snap_key, snap))
344 return result
345
346 def lookup_snap(self, snap_key):
347 snaps = self._data.get('snaps')
348 if not snaps or snap_key not in snaps:
349 return None
350 return KernelSnapEntry(self._ks, self, snap_key, snaps[snap_key])
351
352 @property
353 def derived_from(self):
354 if 'derived-from' not in self._data:
355 return None
356
357 (series_key, source_key) = self._data['derived-from']
358
359 series = self._ks.lookup_series(series_key)
360 source = series.lookup_source(source_key)
361
362 return source
363
364 @property
365 def testable_flavours(self):
366 retval = []
367 if (self._data.get('testing') is not None and
368 self._data['testing'].get('flavours') is not None
369 ):
370 for flavour in self._data['testing']['flavours'].keys():
371 fdata = self._data['testing']['flavours'][flavour]
372 # If we have neither arches nor clouds we represent a noop
373 if not fdata:
374 continue
375 arches = fdata.get('arches', None)
376 arches = arches if arches is not None else []
377 clouds = fdata.get('clouds', None)
378 clouds = clouds if clouds is not None else []
379 retval.append(KernelSourceTestingFlavourEntry(flavour, arches, clouds))
380 return retval
381
382 @property
383 def invalid_tasks(self):
384 retval = self._data.get('invalid-tasks', [])
385 if retval is None:
386 retval = []
387 return retval
388
389 @property
390 def copy_forward(self):
391 if 'copy-forward' not in self._data:
392 return None
393
394 # XXX: backwards compatibility.
395 if self._data['copy-forward'] is False:
396 return None
397 if self._data['copy-forward'] is True:
398 derived_from = self.derived_from
399 if derived_from is None:
400 return True
401 return self.derived_from
402
403 (series_key, source_key) = self._data['copy-forward']
404
405 series = self._ks.lookup_series(series_key)
406 source = series.lookup_source(source_key)
407
408 return source
409
410 @property
411 def backport(self):
412 return self._data.get('backport', False)
413
414 @property
415 def routing(self):
416 default = 'default'
417 if self.series.development:
418 default = 'devel'
419 if self.series.esm:
420 default = 'esm'
421 data = self._data.get('routing', default)
422 if data is None:
423 return data
424 return KernelRoutingEntry(self._ks, self, data)
425
426 @property
427 def swm_data(self):
428 return self._data.get('swm')
429
430 @property
431 def private(self):
432 return self._data.get('private', False)
433
434 def __str__(self):
435 return "{} {}".format(self.series.name, self.name)
436
437class KernelSourceTestingFlavourEntry:
438 def __init__(self, name, arches, clouds):
439 self._name = name
440 self._arches = arches
441 self._clouds = clouds
442
443 @property
444 def name(self):
445 return self._name
446
447 @property
448 def arches(self):
449 return self._arches
450
451 @property
452 def clouds(self):
453 return self._clouds
454
455class KernelSeriesEntry:
456 def __init__(self, ks, name, data, defaults=None):
457 self._ks = ks
458 self._name = name
459 self._data = {}
460 if defaults is not None:
461 self._data.update(defaults)
462 if data is not None:
463 self._data.update(data)
464
465 def __eq__(self, other):
466 if isinstance(self, other.__class__):
467 return self.name == other.name
468 return False
469
470 def __ne__(self, other):
471 return not self.__eq__(other)
472
473 @property
474 def name(self):
475 return self._name
476
477 @property
478 def codename(self):
479 return self._data.get('codename', None)
480
481 @property
482 def opening(self):
483 if 'opening' in self._data:
484 if self._data['opening'] is not False:
485 return True
486 return False
487
488 def opening_ready(self, *flags):
489 if 'opening' not in self._data:
490 return True
491 allow = self._data['opening']
492 if allow is None:
493 return False
494 if allow in (True, False):
495 return not allow
496 for flag in flags:
497 flag_allow = allow.get(flag, False)
498 if flag_allow is None or flag_allow is False:
499 return False
500 return True
501 opening_allow = opening_ready
502
503 @property
504 def development(self):
505 return self._data.get('development', False)
506
507 @property
508 def supported(self):
509 return self._data.get('supported', False)
510
511 @property
512 def lts(self):
513 return self._data.get('lts', False)
514
515 @property
516 def esm(self):
517 return self._data.get('esm', False)
518
519 def __str__(self):
520 return "{} ({})".format(self.name, self.codename)
521
522 @property
523 def sources(self):
524 result = []
525 sources = self._data.get('sources')
526 if sources:
527 for source_key, source in sources.items():
528 result.append(KernelSourceEntry(
529 self._ks, self, source_key, source))
530 return result
531
532 @property
533 def routing_table(self):
534 return self._data.get('routing-table', None)
535
536 def lookup_source(self, source_key):
537 sources = self._data.get('sources')
538 if not sources or source_key not in sources:
539 return None
540 return KernelSourceEntry(self._ks, self, source_key, sources[source_key])
541
542
543# KernelSeries
544#
545class KernelSeries:
546 _url = 'https://git.launchpad.net/~canonical-kernel/' \
547 '+git/kteam-tools/plain/info/kernel-series.yaml'
548 _url_local = 'file://' + os.path.realpath(os.path.join(os.path.dirname(__file__),
549 '..', 'info', 'kernel-series.yaml'))
550 #_url = 'file:///home/apw/git2/kteam-tools/info/kernel-series.yaml'
551 #_url = 'file:///home/work/kteam-tools/info/kernel-series.yaml'
552 _data_txt = {}
553
554 @classmethod
555 def __load_once(cls, url):
556 if url not in cls._data_txt:
557 response = urlopen(url)
558 data = response.read()
559 if not isinstance(data, str):
560 data = data.decode('utf-8')
561 cls._data_txt[url] = data
562 return cls._data_txt[url]
563
564 def __init__(self, url=None, data=None, use_local=os.getenv("USE_LOCAL_KERNEL_SERIES_YAML", False)):
565 if data or url:
566 if url:
567 response = urlopen(url)
568 data = response.read()
569 if not isinstance(data, str):
570 data = data.decode('utf-8')
571 else:
572 data = self.__load_once(self._url_local if use_local else self._url)
573 self._data = yaml.load(data)
574
575 self._development_series = None
576 self._codename_to_series = {}
577 for series_key, series in self._data.items():
578 if not series:
579 continue
580 if series.get('development', False):
581 self._development_series = series_key
582 if 'codename' in series:
583 self._codename_to_series[series['codename']] = series_key
584
585 # Pull out the defaults.
586 self._defaults_series = {}
587 if 'defaults' in self._data:
588 self._defaults_series = self._data['defaults']
589 del self._data['defaults']
590
591 @staticmethod
592 def key_series_name(series):
593 return [int(x) for x in series.name.split('.')]
594
595 @property
596 def series(self):
597 return [KernelSeriesEntry(self, series_key, series,
598 defaults=self._defaults_series)
599 for series_key, series in self._data.items()]
600
601 def lookup_series(self, series=None, codename=None, development=False):
602 if not series and not codename and not development:
603 raise ValueError("series/codename/development required")
604 if not series and codename:
605 if codename not in self._codename_to_series:
606 return None
607 series = self._codename_to_series[codename]
608 if not series and development:
609 if not self._development_series:
610 return None
611 series = self._development_series
612 if series and series not in self._data:
613 return None
614 return KernelSeriesEntry(self, series, self._data[series],
615 defaults=self._defaults_series)
616
617
618if __name__ == '__main__':
619 db = KernelSeries()
620
621 series = db.lookup_series('16.04')
622 if series.name != '16.04':
623 print('series.name != 16.04')
624 if series.codename != 'xenial':
625 print('series.codename != xenial')
626
627 series2 = db.lookup_series(codename='xenial')
628 if series2.name != '16.04':
629 print('series2.name != 16.04')
630 if series2.codename != 'xenial':
631 print('series2.codename != xenial')
632
633 series3 = db.lookup_series(development=True)
634 if series3.name != '18.04':
635 print('series3.name != 18.04')
636 if series3.codename != 'bionic':
637 print('series3.codename != bionic')
638
639 print(str(series), str(series2), str(series3))
640
641 for series2 in sorted(db.series, key=db.key_series_name):
642 print(series2)
643
644 for source in series.sources:
645 print(str(source), source.series.name, source.name)
646
647 print(source.derived_from)
648 print(source.versions)
649
650 for package in source.packages:
651 print("PACKAGE", str(package))
652
653 for snap in source.snaps:
654 print("SNAP", str(snap), snap.arches)
655
656
657# vi:set ts=4 sw=4 expandtab:
0658
=== modified file 'sru-release'
--- sru-release 2019-08-27 11:30:27 +0000
+++ sru-release 2019-09-19 13:51:06 +0000
@@ -39,6 +39,8 @@
3939
40from launchpadlib.launchpad import Launchpad40from launchpadlib.launchpad import Launchpad
4141
42from kernel_series import KernelSeries
43
4244
43# Each entry in this list is a list of source packages that are known45# Each entry in this list is a list of source packages that are known
44# to have inter-dependencies and must be released simultaneously.46# to have inter-dependencies and must be released simultaneously.
@@ -179,10 +181,10 @@
179 'published': proposed_date}181 'published': proposed_date}
180 '''182 '''
181 versions = defaultdict(dict)183 versions = defaultdict(dict)
182 if options.esm:184 if src_archive.reference == 'ubuntu':
185 pocket = 'Proposed'
186 else:
183 pocket = 'Release'187 pocket = 'Release'
184 else:
185 pocket = 'Proposed'
186188
187 matches = src_archive.getPublishedSources(189 matches = src_archive.getPublishedSources(
188 source_name=sourcename, exact_match=not options.pattern,190 source_name=sourcename, exact_match=not options.pattern,
@@ -193,9 +195,9 @@
193 source_name=match.source_package_name, exact_match=True,195 source_name=match.source_package_name, exact_match=True,
194 status='Published', distro_series=series):196 status='Published', distro_series=series):
195 key = pub.pocket.lower()197 key = pub.pocket.lower()
196 # special case for ESM ppas, which don't have pockets but need198 # special case for ppas, which don't have pockets but need
197 # to be treated as -proposed199 # to be treated as -proposed
198 if options.esm and key == 'release':200 if pocket == 'Release' and key == 'release':
199 key = 'proposed'201 key = 'proposed'
200 versions[pub.source_package_name][key] = (202 versions[pub.source_package_name][key] = (
201 pub.source_package_version)203 pub.source_package_version)
@@ -213,7 +215,6 @@
213 if pocket in pub.pocket:215 if pocket in pub.pocket:
214 versions[pub.source_package_name]['changesfile'] = (216 versions[pub.source_package_name]['changesfile'] = (
215 pub.changesFileUrl())217 pub.changesFileUrl())
216
217 # devel version218 # devel version
218 if devel_series:219 if devel_series:
219 for pub in src_archive.getPublishedSources(220 for pub in src_archive.getPublishedSources(
@@ -360,6 +361,13 @@
360 release = args.pop(0)361 release = args.pop(0)
361 packages = args362 packages = args
362363
364 # XXX: we only want to instantiate KernelSeries if we suspect this is
365 # a kernel package, this is necessarily dirty, dirty, dirty.
366 kernel_checks = False
367 for package in packages:
368 if package.startswith('linux-') or package == 'linux':
369 kernel_checks = True
370
363 if not options.skip_package_group_check:371 if not options.skip_package_group_check:
364 try:372 try:
365 packages = check_package_sets(packages)373 packages = check_package_sets(packages)
@@ -376,12 +384,57 @@
376 sys.stderr.write(384 sys.stderr.write(
377 'WARNING: No current development series, -d will not work\n')385 'WARNING: No current development series, -d will not work\n')
378 devel_series = None386 devel_series = None
379 if release in ('precise', 'trusty'):387
388 ks_source = None
389 if kernel_checks:
390 kernel_series = KernelSeries()
391
392 # See if we have a kernel-series record for this package. If we do
393 # then we are going to pivot to the routing therein.
394 ks_series = kernel_series.lookup_series(codename=release)
395 for ks_source_find in ks_series.sources:
396 for ks_package in ks_source_find.packages:
397 if ks_package.name == packages[0]:
398 ks_source = ks_source_find
399 break
400
401 # First confirm everything in this set we are attempting to release
402 # are indeed listed as valid for this kernel.
403 if ks_source is not None:
404 for package in packages:
405 if ks_source.lookup_package(package) is None:
406 sys.stderr.write(
407 'WARNING: {} not found in packages for kernel {}\n'.format(
408 package, ks_source.name))
409
410 if ks_source is None and release in ('precise', 'trusty'):
380 sys.stdout.write(411 sys.stdout.write(
381 'Called for {}; assuming kernel ESM publication\n'.format(release))412 'Called for {}; assuming kernel ESM publication\n'.format(release))
382 options.esm = True413 options.esm = True
383414
384 if options.esm:415 # If we found a KernelSeries entry this has accurate routing information
416 # attached use that.
417 if ks_source is not None:
418 src_archive_ref, src_archive_pocket = ks_source.routing.lookup_destination('proposed', primary=True)
419 src_archive = launchpad.archives.getByReference(
420 reference=src_archive_ref)
421 dst_archive_ref, dst_archive_pocket = ks_source.routing.lookup_destination('updates', primary=True)
422 if dst_archive_ref == src_archive_ref:
423 dst_archive = src_archive
424 else:
425 dst_archive = launchpad.archives.getByReference(
426 reference=dst_archive_ref)
427
428 # Announce any non-standard archive routing.
429 if src_archive_ref != 'ubuntu':
430 print("Src Archive: {}".format(src_archive_ref))
431 if dst_archive_ref != 'ubuntu':
432 print("Dst Archive: {}".format(dst_archive_ref))
433 # --security is meaningless for private PPA publishing (XXX: currently true)
434 options.security = False
435 options.release = True
436
437 elif options.esm:
385 # --security is meaningless for ESM everything is a security update.438 # --security is meaningless for ESM everything is a security update.
386 options.security = False439 options.security = False
387 options.release = True440 options.release = True

Subscribers

People subscribed via source and target branches