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
1=== modified file 'copy-proposed-kernel'
2--- copy-proposed-kernel 2019-08-27 11:32:43 +0000
3+++ copy-proposed-kernel 2019-09-19 13:51:06 +0000
4@@ -32,12 +32,16 @@
5
6 from launchpadlib.launchpad import Launchpad
7
8+from kernel_series import KernelSeries
9+
10
11 class TestBase(unittest.TestCase):
12 class FakeArgs:
13 def __init__(self, **kwargs):
14+ self.testing = True
15 self.series = None
16 self.source = None
17+ self.ppa2 = False
18 self.security = False
19 self.security2 = False
20 self.esm = False
21@@ -45,6 +49,7 @@
22 self.ibmgt = False
23 self.to_signing = False
24 self.from_signing = False
25+ self.no_auto = False
26
27 self.update(**kwargs)
28
29@@ -63,56 +68,139 @@
30 finally:
31 sys.stdout, sys.stderr = old_out, old_err
32
33+ @classmethod
34+ def setUpClass(cls):
35+ data = """
36+ defaults:
37+ routing-table:
38+ default:
39+ security-build:
40+ - ['ppa:canonical-kernel-security-team/ubuntu/ppa', 'Release' ]
41+ - ['ppa:canonical-kernel-security-team/ubuntu/ppa2', 'Release' ]
42+ build:
43+ - ['ppa:canonical-kernel-team/ubuntu/ppa', 'Release' ]
44+ proposed:
45+ - ['ubuntu', 'Proposed' ]
46+ esm:
47+ security-build:
48+ - ['ppa:canonical-kernel-security-team/ubuntu/esm', 'Release']
49+ build:
50+ - ['ppa:canonical-kernel-esm/ubuntu/ppa', 'Release']
51+ signing:
52+ - ['ppa:canonical-signing/ubuntu/esm', 'Release']
53+ proposed:
54+ - ['ppa:canonical-kernel-esm/ubuntu/proposed', 'Release']
55+ 14.04:
56+ codename: trusty
57+ supported: true
58+ esm: true
59+ sources:
60+ linux:
61+ packages:
62+ linux:
63+ linux-signed:
64+ type: signed
65+ linux-meta:
66+ type: meta
67+ 16.04:
68+ codename: xenial
69+ supported: true
70+ sources:
71+ linux-fips:
72+ routing:
73+ security-build:
74+ - ['ppa:canonical-kernel-security-team/ubuntu/ppa', 'Release']
75+ - ['ppa:canonical-kernel-security-team/ubuntu/ppa2', 'Release']
76+ build:
77+ - ['ppa:fips-cc-stig/ubuntu/fips-build', 'Release']
78+ signing:
79+ - ['ppa:canonical-signing/ubuntu/fips', 'Release']
80+ proposed:
81+ - ['ppa:ubuntu-advantage/ubuntu/fips-proposed', 'Release']
82+ packages:
83+ linux-fips:
84+ linux-meta-fips:
85+ type: meta
86+ linux-signed-fips:
87+ type: signed
88+ 18.04:
89+ codename: bionic
90+ supported: true
91+ sources:
92+ linux:
93+ packages:
94+ linux:
95+ linux-signed:
96+ type: signed
97+ linux-meta:
98+ type: meta
99+ linux-ibm-gt:
100+ routing:
101+ security-build:
102+ - ['ppa:canonical-kernel-security-team/ubuntu/ppa', 'Release']
103+ - ['ppa:canonical-kernel-security-team/ubuntu/ppa2', 'Release']
104+ build:
105+ - ['ppa:ibm-cloud/ubuntu/build', 'Release']
106+ proposed:
107+ - ['ppa:ibm-cloud/ubuntu/proposed', 'Release']
108+ packages:
109+ linux-ibm-gt:
110+ linux-meta-ibm-gt:
111+ type: meta
112+ """
113+ cls.ks = KernelSeries(data=data)
114+
115
116 class TestRouting(TestBase):
117 def test_default(self):
118- expected = ('~canonical-kernel-team/ubuntu/ppa', 'ubuntu', False)
119- result = routing(self.FakeArgs())
120+ expected = (['ppa:canonical-kernel-team/ubuntu/ppa', 'Release'], ['ubuntu', 'Proposed'], False)
121+ result = routing(self.FakeArgs(series='bionic', source='linux'), self.ks)
122 self.assertEqual(expected, result)
123
124 def test_security(self):
125- expected = ('~canonical-kernel-security-team/ubuntu/ppa', 'ubuntu', True)
126- result = routing(self.FakeArgs(security=True))
127+ expected = (['ppa:canonical-kernel-security-team/ubuntu/ppa', 'Release'], ['ubuntu', 'Proposed'], True)
128+ result = routing(self.FakeArgs(series='bionic', source='linux', security=True), self.ks)
129 self.assertEqual(expected, result)
130
131 def test_security2(self):
132- expected = ('~canonical-kernel-security-team/ubuntu/ppa2', 'ubuntu', True)
133- result = routing(self.FakeArgs(security2=True))
134+ expected = (['ppa:canonical-kernel-security-team/ubuntu/ppa2', 'Release'], ['ubuntu', 'Proposed'], True)
135+ result = routing(self.FakeArgs(series='bionic', source='linux', security2=True), self.ks)
136 self.assertEqual(expected, result)
137
138 def test_to_signing(self):
139- expected = ('~canonical-kernel-team/ubuntu/ppa', None, False)
140- result = routing(self.FakeArgs(to_signing=True))
141+ expected = (['ppa:canonical-kernel-team/ubuntu/ppa', 'Release'], None, False)
142+ result = routing(self.FakeArgs(series='bionic', source='linux', to_signing=True), self.ks)
143 self.assertEqual(expected, result)
144
145 def test_from_signing(self):
146- expected = (None, 'ubuntu', False)
147- result = routing(self.FakeArgs(from_signing=True))
148+ expected = (None, ['ubuntu', 'Proposed'], False)
149+ result = routing(self.FakeArgs(series='bionic', source='linux', from_signing=True), self.ks)
150 self.assertEqual(expected, result)
151
152 def test_esm(self):
153- expected = ('~canonical-kernel-esm/ubuntu/ppa', '~canonical-kernel-esm/ubuntu/proposed', False)
154- result = routing(self.FakeArgs(esm=True))
155+ expected = (['ppa:canonical-kernel-esm/ubuntu/ppa', 'Release'], ['ppa:canonical-signing/ubuntu/esm', 'Release'], False)
156+ result = routing(self.FakeArgs(series='trusty', source='linux'), self.ks)
157 self.assertEqual(expected, result)
158
159 def test_esm_security(self):
160- expected = ('~canonical-kernel-security-team/ubuntu/esm', '~canonical-kernel-esm/ubuntu/proposed', False)
161- result = routing(self.FakeArgs(esm=True, security=True))
162+ expected = (['ppa:canonical-kernel-security-team/ubuntu/esm', 'Release'], ['ppa:canonical-signing/ubuntu/esm', 'Release'], False)
163+ result = routing(self.FakeArgs(series='trusty', source='linux', security=True), self.ks)
164 self.assertEqual(expected, result)
165
166 def test_esm_security2(self):
167- expected = (None, '~canonical-kernel-esm/ubuntu/proposed', False)
168- result = routing(self.FakeArgs(esm=True, security2=True))
169- self.assertEqual(expected, result)
170+ with self.assertRaises(SystemExit), self.capture() as (out, err):
171+ expected = (None, ['ppa:canonical-kernel-esm/ubuntu/proposed', 'Release'], False)
172+ result = routing(self.FakeArgs(series='trusty', source='linux', security2=True), self.ks)
173+ self.assertEqual(expected, result)
174
175 def test_esm_to_signing(self):
176- expected = ('~canonical-kernel-esm/ubuntu/ppa', '~canonical-signing/ubuntu/esm', False)
177- result = routing(self.FakeArgs(esm=True, to_signing=True))
178+ expected = (['ppa:canonical-kernel-esm/ubuntu/ppa', 'Release'], ['ppa:canonical-signing/ubuntu/esm', 'Release'], False)
179+ result = routing(self.FakeArgs(series='trusty', source='linux', esm=True, to_signing=True), self.ks)
180 self.assertEqual(expected, result)
181
182 def test_esm_from_signing(self):
183- expected = ('~canonical-signing/ubuntu/esm', '~canonical-kernel-esm/ubuntu/proposed', False)
184- result = routing(self.FakeArgs(esm=True, from_signing=True))
185+ expected = (['ppa:canonical-signing/ubuntu/esm', 'Release'], ['ppa:canonical-kernel-esm/ubuntu/proposed', 'Release'], False)
186+ result = routing(self.FakeArgs(series='trusty', source='linux', esm=True, from_signing=True), self.ks)
187 self.assertEqual(expected, result)
188
189 # Autorouting will enable to_signing, the user will then want to switch us
190@@ -120,226 +208,145 @@
191 # simple we make from_signing take presidence over to_signing. Test this
192 # is honoured correctly.
193 def test_esm_from_signing_override_to_signing(self):
194- expected = ('~canonical-signing/ubuntu/esm', '~canonical-kernel-esm/ubuntu/proposed', False)
195- result = routing(self.FakeArgs(esm=True, to_signing=True, from_signing=True))
196+ expected = (['ppa:canonical-signing/ubuntu/esm', 'Release'], ['ppa:canonical-kernel-esm/ubuntu/proposed', 'Release'], False)
197+ result = routing(self.FakeArgs(series='trusty', source='linux', esm=True, to_signing=True, from_signing=True), self.ks)
198 self.assertEqual(expected, result)
199
200 def test_fips(self):
201- expected = ('~fips-cc-stig/ubuntu/fips-build', '~ubuntu-advantage/ubuntu/fips-proposed', False)
202- result = routing(self.FakeArgs(fips=True))
203+ expected = (['ppa:fips-cc-stig/ubuntu/fips-build', 'Release'], ['ppa:canonical-signing/ubuntu/fips', 'Release'], False)
204+ result = routing(self.FakeArgs(series='xenial', source='linux-fips'), self.ks)
205 self.assertEqual(expected, result)
206
207 def test_fips_security(self):
208- expected = ('~canonical-kernel-security-team/ubuntu/ppa', '~ubuntu-advantage/ubuntu/fips-proposed', False)
209- result = routing(self.FakeArgs(fips=True, security=True))
210+ expected = (['ppa:canonical-kernel-security-team/ubuntu/ppa', 'Release'], ['ppa:canonical-signing/ubuntu/fips', 'Release'], False)
211+ result = routing(self.FakeArgs(series='xenial', source='linux-fips', security=True), self.ks)
212 self.assertEqual(expected, result)
213
214 def test_fips_security2(self):
215- expected = ('~canonical-kernel-security-team/ubuntu/ppa2', '~ubuntu-advantage/ubuntu/fips-proposed', False)
216- result = routing(self.FakeArgs(fips=True, security2=True))
217+ expected = (['ppa:canonical-kernel-security-team/ubuntu/ppa2', 'Release'], ['ppa:canonical-signing/ubuntu/fips', 'Release'], False)
218+ result = routing(self.FakeArgs(series='xenial', source='linux-fips', security2=True), self.ks)
219 self.assertEqual(expected, result)
220
221 def test_fips_to_signing(self):
222- expected = ('~fips-cc-stig/ubuntu/fips-build', '~canonical-signing/ubuntu/fips', False)
223- result = routing(self.FakeArgs(fips=True, to_signing=True))
224+ expected = (['ppa:fips-cc-stig/ubuntu/fips-build', 'Release'], ['ppa:canonical-signing/ubuntu/fips', 'Release'], False)
225+ result = routing(self.FakeArgs(series='xenial', source='linux-fips', to_signing=True), self.ks)
226 self.assertEqual(expected, result)
227
228 def test_fips_from_signing(self):
229- expected = ('~canonical-signing/ubuntu/fips', '~ubuntu-advantage/ubuntu/fips-proposed', False)
230- result = routing(self.FakeArgs(fips=True, from_signing=True))
231+ expected = (['ppa:canonical-signing/ubuntu/fips', 'Release'], ['ppa:ubuntu-advantage/ubuntu/fips-proposed', 'Release'], False)
232+ result = routing(self.FakeArgs(series='xenial', source='linux-fips', from_signing=True), self.ks)
233 self.assertEqual(expected, result)
234
235 def test_ibmgt(self):
236- expected = ('~ibm-cloud/ubuntu/build', '~ibm-cloud/ubuntu/proposed', False)
237- result = routing(self.FakeArgs(ibmgt=True))
238+ expected = (['ppa:ibm-cloud/ubuntu/build', 'Release'], ['ppa:ibm-cloud/ubuntu/proposed', 'Release'], False)
239+ result = routing(self.FakeArgs(series='bionic', source='linux-ibm-gt'), self.ks)
240 self.assertEqual(expected, result)
241
242 def test_ibmgt_security(self):
243- expected = ('~canonical-kernel-security-team/ubuntu/ppa', '~ibm-cloud/ubuntu/proposed', False)
244- result = routing(self.FakeArgs(ibmgt=True, security=True))
245+ expected = (['ppa:canonical-kernel-security-team/ubuntu/ppa', 'Release'], ['ppa:ibm-cloud/ubuntu/proposed', 'Release'], False)
246+ result = routing(self.FakeArgs(series='bionic', source='linux-ibm-gt', security=True), self.ks)
247 self.assertEqual(expected, result)
248
249 def test_ibmgt_security2(self):
250- expected = ('~canonical-kernel-security-team/ubuntu/ppa2', '~ibm-cloud/ubuntu/proposed', False)
251- result = routing(self.FakeArgs(ibmgt=True, security2=True))
252+ expected = (['ppa:canonical-kernel-security-team/ubuntu/ppa2', 'Release'], ['ppa:ibm-cloud/ubuntu/proposed', 'Release'], False)
253+ result = routing(self.FakeArgs(series='bionic', source='linux-ibm-gt', security2=True), self.ks)
254 self.assertEqual(expected, result)
255
256
257-def routing(args):
258+def routing(args, ks):
259+ series_name = args.series
260+ package_name = args.source
261+
262+ series = ks.lookup_series(codename=series_name)
263+ if series is None:
264+ print("ERROR: {} -- series unknown".format(series_name))
265+ sys.exit(1)
266+
267+ package = None
268+ package_signed = None
269+ for source_srch in series.sources:
270+ package_signed = None
271+ for package_srch in source_srch.packages:
272+ if package_srch.name == package_name:
273+ package = package_srch
274+ if (package_srch.name.startswith('linux-signed-') or
275+ package_srch.name == 'linux-signed'):
276+ package_signed = package_srch
277+ if package is not None:
278+ break
279+ if package is None:
280+ print("ERROR: {}/{} -- package unknown".format(series_name, package_name))
281+ sys.exit(1)
282+
283+ source = package.source
284+ routing = source.routing
285+ if routing is None:
286+ print("ERROR: {}/{} -- package has no routing".format(series_name, package_name))
287+ sys.exit(1)
288+
289+ build_archives = routing.lookup_destination('build')
290+ security_archives = routing.lookup_destination('security-build')
291+ proposed_archive = routing.lookup_destination('proposed', primary=True)
292+ signing_archive = routing.lookup_destination('signing', primary=True)
293+
294+ if build_archives is None or len(build_archives) < 1:
295+ print("ERROR: {}/{} -- package has no primary build archive".format(series_name, package_name))
296+ sys.exit(1)
297+ if args.ppa2 and (build_archives is None or len(build_archives) < 2):
298+ print("ERROR: {}/{} -- package has no secondary build archive".format(series_name, package_name))
299+ sys.exit(1)
300+ if build_archives is None:
301+ print("ERROR: {}/{} -- package has no build archive".format(series_name, package_name))
302+ sys.exit(1)
303+ if proposed_archive is None:
304+ print("ERROR: {}/{} -- package has no proposed archive".format(series_name, package_name))
305+ sys.exit(1)
306+ if args.security and (security_archives is None or len(security_archives) < 1):
307+ print("ERROR: {}/{} -- package has no primary security archive".format(series_name, package_name))
308+ sys.exit(1)
309+ if args.security2 and (security_archives is None or len(security_archives) < 2):
310+ print("ERROR: {}/{} -- package has no secondary security archive".format(series_name, package_name))
311+ sys.exit(1)
312+
313+ # Default route build -> proposed
314+ if args.ppa2:
315+ from_archive = build_archives[1]
316+ else:
317+ from_archive = build_archives[0]
318+ to_archive = proposed_archive
319+
320 unembargo = False
321
322- # Default routing.
323- security_name = (
324- '~canonical-kernel-security-team/ubuntu/ppa',
325- '~canonical-kernel-security-team/ubuntu/ppa2',
326- )
327- signing = None
328-
329- # ESM specific routing
330- if args.esm:
331- ppa_name = '~canonical-kernel-esm/ubuntu/ppa'
332- to = '~canonical-kernel-esm/ubuntu/proposed'
333- signing = '~canonical-signing/ubuntu/esm'
334- security_name = (
335- '~canonical-kernel-security-team/ubuntu/esm',
336- None,
337- )
338-
339- # FIPS specific routing
340- elif args.fips:
341- ppa_name = '~fips-cc-stig/ubuntu/fips-build'
342- to = '~ubuntu-advantage/ubuntu/fips-proposed'
343- signing = '~canonical-signing/ubuntu/fips'
344-
345- # IBMGT specific routing
346- elif args.ibmgt:
347- ppa_name = '~ibm-cloud/ubuntu/build'
348- to = '~ibm-cloud/ubuntu/proposed'
349-
350- # Default routing.
351- else:
352- ppa_name = '~canonical-kernel-team/ubuntu/ppa'
353- to = 'ubuntu'
354- if args.security or args.security2:
355- # Allow us to unembargo when releasing from security to ubuntu.
356- unembargo = True
357-
358 # Handle security routing.
359 if args.security:
360- ppa_name = security_name[0]
361- elif args.security2:
362- ppa_name = security_name[1]
363+ from_archive = security_archives[0]
364+ if args.security2:
365+ from_archive = security_archives[1]
366+
367+ # Allow us to unembargo when releasing from security to ubuntu.
368+ if (args.security or args.security2) and to_archive[0] == 'ubuntu':
369+ unembargo = True
370
371 # Handle signing routing.
372 if args.from_signing:
373- ppa_name = signing
374+ from_archive = signing_archive
375 elif args.to_signing:
376- to = signing
377-
378- return (ppa_name, to, unembargo)
379-
380-class TestAutoRoute(TestBase):
381- def test_default(self):
382- results = self.FakeArgs(series='bionic', source='linux')
383- expected = copy(results).update()
384- with self.capture() as (out, err):
385- auto_route(results)
386- self.assertEqual(expected.__dict__, results.__dict__)
387- self.assertEqual('', out.getvalue().strip())
388-
389- def test_esm_precise_linux(self):
390- results = self.FakeArgs(series='precise', source='linux')
391- expected = copy(results).update(esm=True)
392- with self.capture() as (out, err):
393- auto_route(results)
394- self.assertEqual(expected.__dict__, results.__dict__)
395- self.assertIn('from and to ESM', out.getvalue().strip())
396- self.assertNotIn('via signing', out.getvalue().strip())
397-
398- def test_esm_precise_linux_meta(self):
399- results = self.FakeArgs(series='precise', source='linux-meta')
400- expected = copy(results).update(esm=True)
401- with self.capture() as (out, err):
402- auto_route(results)
403- self.assertEqual(expected.__dict__, results.__dict__)
404- self.assertIn('from and to ESM', out.getvalue().strip())
405- self.assertNotIn('via signing', out.getvalue().strip())
406-
407- def test_esm_precise_lbm(self):
408- results = self.FakeArgs(series='precise', source='linux-backports-modules-3.2.0')
409- expected = copy(results).update(esm=True)
410- with self.capture() as (out, err):
411- auto_route(results)
412- self.assertEqual(expected.__dict__, results.__dict__)
413- self.assertIn('from and to ESM', out.getvalue().strip())
414- self.assertNotIn('via signing', out.getvalue().strip())
415-
416- def test_esm_precise_linux_lts_trusty(self):
417- results = self.FakeArgs(series='precise', source='linux-lts-trusty')
418- expected = copy(results).update(esm=True, to_signing=True)
419- with self.capture() as (out, err):
420- auto_route(results)
421- self.assertEqual(expected.__dict__, results.__dict__)
422- self.assertIn('from and to ESM', out.getvalue().strip())
423- self.assertIn('via signing', out.getvalue().strip())
424-
425- def test_esm_trusty_linux(self):
426- results = self.FakeArgs(series='trusty', source='linux')
427- expected = copy(results).update(esm=True, to_signing=True)
428- with self.capture() as (out, err):
429- auto_route(results)
430- self.assertEqual(expected.__dict__, results.__dict__)
431- self.assertIn('from and to ESM', out.getvalue().strip())
432- self.assertIn('via signing', out.getvalue().strip())
433-
434- def test_esm_trusty_linux_aws(self):
435- results = self.FakeArgs(series='trusty', source='linux-aws')
436- expected = copy(results).update(esm=True)
437- with self.capture() as (out, err):
438- auto_route(results)
439- self.assertEqual(expected.__dict__, results.__dict__)
440- self.assertIn('from and to ESM', out.getvalue().strip())
441- self.assertNotIn('via signing', out.getvalue().strip())
442-
443- def test_esm_precise_linux_lts_trusty(self):
444- results = self.FakeArgs(series='precise', source='linux-lts-trusty')
445- expected = copy(results).update(esm=True, to_signing=True)
446- with self.capture() as (out, err):
447- auto_route(results)
448- self.assertEqual(expected.__dict__, results.__dict__)
449- self.assertIn('from and to ESM', out.getvalue().strip())
450- self.assertIn('via signing', out.getvalue().strip())
451-
452- def test_fips_bionic_linux_fips(self):
453- results = self.FakeArgs(series='bionic', source='linux-fips')
454- expected = copy(results).update(fips=True, to_signing=True)
455- with self.capture() as (out, err):
456- auto_route(results)
457- self.assertEqual(expected.__dict__, results.__dict__)
458- self.assertIn('from and to FIPS', out.getvalue().strip())
459- self.assertIn('via signing', out.getvalue().strip())
460-
461- def test_ibmgt_bionic_linux_ibmgt(self):
462- results = self.FakeArgs(series='bionic', source='linux-ibm-gt')
463- expected = copy(results).update(ibmgt=True)
464- with self.capture() as (out, err):
465- auto_route(results)
466- self.assertEqual(expected.__dict__, results.__dict__)
467- self.assertIn('from and to IBM-GT', out.getvalue().strip())
468- self.assertNotIn('via signing', out.getvalue().strip())
469-
470-def auto_route(args):
471- before = (args.esm, args.fips, args.ibmgt, args.to_signing)
472-
473- if args.series in ('precise', 'trusty'):
474- args.esm = True
475- if args.esm:
476- if (args.series == 'precise' and args.source in ('linux', 'linux-meta', 'linux-backports-modules-3.2.0') or
477- args.series == 'trusty' and args.source in ('linux-aws', 'linux-meta-aws')):
478- args.to_signing = False
479- else:
480- args.to_signing = True
481-
482- if args.source.endswith('-fips'):
483- args.fips = True
484- if args.fips:
485- args.to_signing = True
486-
487- if args.source.endswith('-ibm-gt'):
488- args.ibmgt = True
489-
490- if before != (args.esm, args.fips, args.ibmgt, args.to_signing):
491- msg = "NOTE: directing copy "
492- if args.esm:
493- msg += "from and to ESM"
494- elif args.fips:
495- msg += "from and to FIPS"
496- elif args.ibmgt:
497- msg += "from and to IBM-GT PPAs"
498- if args.to_signing:
499- msg += " via signing"
500+ to_archive = signing_archive
501+ # Automatically route to signing by default.
502+ elif args.no_auto is False and signing_archive is not None and package_signed is not None:
503+ to_archive = signing_archive
504+
505+ # Announce the routing if needed.
506+ if (args.testing is False and (routing.name != 'default' or from_archive == signing_archive or to_archive == signing_archive)):
507+ msg = "NOTE: directing copy using {} routes".format(routing.name)
508+ if from_archive == signing_archive:
509+ msg += ' from signing'
510+ elif to_archive == signing_archive:
511+ msg += ' to signing'
512 print(msg)
513
514+ return (from_archive, to_archive, unembargo)
515+
516
517 # SELF-TESTS:
518 if len(sys.argv) >= 2 and sys.argv[1] == '--self-test':
519@@ -347,7 +354,9 @@
520 sys.exit(0)
521
522 parser = argparse.ArgumentParser(description='Copy a proposed kernel to the apropriate archive pocket')
523+parser.set_defaults(testing=False)
524 parser.add_argument('--dry-run', action='store_true', help='Do everything but actually copy the package')
525+parser.add_argument('--ppa2', action='store_true', help='Copy from the kernel build PPA2')
526 parser.add_argument('--security', '-S', action='store_true', help='Copy from the kernel security PPA')
527 parser.add_argument('--security2', action='store_true', help='Copy from the kernel security PPA2')
528 parser.add_argument('--esm', '-E', action='store_true', help='Copy from the kernel ESM PPA and to the kernel ESM proposed PPA')
529@@ -357,73 +366,87 @@
530 parser.add_argument('--to-signing', action='store_true', help='Copy from the kernel ESM/FIPS PPA to the ESM/FIPS signing PPA')
531 parser.add_argument('--from-signing', action='store_true', help='Copy from the ESM/FIPS signing PPA to the ESM/FIPS proposed PPA')
532 parser.add_argument('series', action='store', help='The series the source package is in')
533-parser.add_argument('source', action='store', help='The source package name')
534+parser.add_argument('source', action='store', nargs='+', help='The source package name')
535
536 args = parser.parse_args()
537
538-# If we are allowed to intuit destinations do so:
539-# 1) precise is now destined for the ESM PPAs
540-if not args.no_auto:
541- auto_route(args)
542-
543-(ppa_name, to, security) = routing(args)
544-
545-##print("ppa_name<{}> to<{}>".format(ppa_name, to))
546-
547-if ppa_name is None:
548- print("ERROR: bad source PPA")
549- sys.exit(1)
550-if to is None:
551- print("ERROR: bad destination")
552- sys.exit(1)
553-
554-(release, pkg) = (args.series, args.source)
555+if args.esm or args.fips or args.ibmgt:
556+ print("NOTE: flags --esm, --fips, and --ibmgt are now deprecated")
557+
558+release = args.series
559+
560+ks = KernelSeries()
561
562 launchpad = Launchpad.login_with(
563 'ubuntu-archive-tools', 'production', version='devel')
564 ubuntu = launchpad.distributions['ubuntu']
565 distro_series = ubuntu.getSeries(name_or_version=release)
566-kernel_ppa = launchpad.archives.getByReference(
567- reference=ppa_name)
568-
569-# Grab a reference to the 'to' archive and select a pocket.
570-to_archive = launchpad.archives.getByReference(reference=to)
571-if to == 'ubuntu':
572- to_pocket = 'proposed'
573-else:
574- to_pocket = 'release'
575-
576-# get current version in PPA for that series
577-versions = kernel_ppa.getPublishedSources(
578- source_name=pkg, exact_match=True, status='Published', pocket='Release',
579- distro_series=distro_series)
580-version = None
581-if versions.total_size == 1:
582- version = versions[0].source_package_version
583-
584-include_binaries = (pkg not in ('debian-installer')
585- and not pkg.startswith('linux-signed'))
586-if args.from_signing:
587- include_binaries = True
588-
589-print("""Copying {}/{}:
590- From: {} release
591- To: {} {}
592- Binaries: {}""".format(pkg, version, kernel_ppa, to_archive, to_pocket, include_binaries))
593-
594-if not version:
595- print("ERROR: no version to copy")
596- sys.exit(1)
597+
598+copies = []
599+for pkg in list(args.source):
600+ # BODGE: routing should just take release/pkg.
601+ args.source = pkg
602+
603+ (from_archive, to_archive, security) = routing(args, ks)
604+ ##print("from_archive<{}> to_archive<{}>".format(from_archive, to_archive))
605+
606+ if from_archive is None:
607+ print("ERROR: bad source PPA")
608+ sys.exit(1)
609+ if to_archive is None:
610+ print("ERROR: bad destination")
611+ sys.exit(1)
612+
613+ (from_reference, from_pocket) = from_archive
614+ (to_reference, to_pocket) = to_archive
615+
616+ # Grab a reference to the 'from' archive.
617+ from_archive = launchpad.archives.getByReference(
618+ reference=from_reference)
619+
620+ # Grab a reference to the 'to' archive.
621+ to_archive = launchpad.archives.getByReference(reference=to_reference)
622+
623+ # get current version in PPA for that series
624+ versions = from_archive.getPublishedSources(
625+ source_name=pkg, exact_match=True, status='Published', pocket=from_pocket,
626+ distro_series=distro_series)
627+ version = None
628+ if versions.total_size == 1:
629+ version = versions[0].source_package_version
630+
631+ include_binaries = (pkg not in ('debian-installer')
632+ and not pkg.startswith('linux-signed'))
633+ if args.from_signing:
634+ include_binaries = True
635+
636+ print("""Copying {}/{}:
637+ From: {} {}
638+ To: {} {}
639+ Binaries: {}""".format(pkg, version, from_archive.reference, from_pocket, to_archive.reference, to_pocket, include_binaries))
640+
641+ if not version:
642+ print("ERROR: no version to copy")
643+ sys.exit(1)
644+
645+ copies.append({
646+ 'from_archive': from_archive,
647+ 'include_binaries': include_binaries,
648+ 'source_name': pkg,
649+ 'to_series': release,
650+ 'to_pocket': to_pocket,
651+ 'version': version,
652+ 'auto_approve': True,
653+ 'unembargo': security,
654+ })
655
656 if args.dry_run:
657 print("Dry run; no packages copied.")
658 sys.exit(0)
659
660-# Finally ready to actually copy this.
661-to_archive.copyPackage(
662- from_archive=kernel_ppa, include_binaries=include_binaries,
663- source_name=pkg, to_series=release, to_pocket=to_pocket, version=version,
664- auto_approve=True, unembargo=security)
665+for copy in copies:
666+ # We found valid packages for each requested element, actually copy them.
667+ to_archive.copyPackage(**copy)
668
669 # TODO: adjust this script to use find-bin-overrides or rewrite
670 # find-bin-overrides to use lpapi and use it here.
671
672=== added file 'kernel_series.py'
673--- kernel_series.py 1970-01-01 00:00:00 +0000
674+++ kernel_series.py 2019-09-19 13:51:06 +0000
675@@ -0,0 +1,657 @@
676+#!/usr/bin/env python
677+#
678+
679+try:
680+ from urllib.request import urlopen
681+except ImportError:
682+ from urllib2 import urlopen
683+
684+import os
685+import yaml
686+
687+class KernelRoutingEntry:
688+ def __init__(self, ks, source, data):
689+ name = "{}:{}".format(source.series.codename, source.name)
690+ if isinstance(data, str):
691+ name = data
692+ table = source.series.routing_table
693+ if table is None:
694+ raise ValueError("unable to map routing alias {}, "
695+ "no series routing table".format(data))
696+ if data not in table:
697+ raise ValueError("unable to map routing alias {}, "
698+ "not listed in series routing table".format(data))
699+ data = table[data]
700+
701+ # Clear out any entries that have been overriden to None.
702+ for entry in dict(data):
703+ if data[entry] is None:
704+ del data[entry]
705+
706+ self._ks = ks
707+ self._source = source
708+ self._name = name
709+ self._data = data if data else {}
710+
711+ @property
712+ def source(self):
713+ return self._source
714+
715+ @property
716+ def name(self):
717+ return self._name
718+
719+ def __eq__(self, other):
720+ if isinstance(self, other.__class__):
721+ return list(self) == list(other)
722+ return False
723+
724+ def __ne__(self, other):
725+ return not self.__eq__(other)
726+
727+ def __iter__(self):
728+ return iter(self._data.items())
729+
730+ def __getitem__(self, which):
731+ return self._data[which]
732+
733+ def lookup_destination(self, dest, primary=False):
734+ data = self._data.get(dest, None)
735+ if primary is False or data is None:
736+ return data
737+ return data[0]
738+
739+ def __str__(self):
740+ return str(self._data)
741+
742+
743+class KernelRepoEntry:
744+ def __init__(self, ks, owner, data):
745+ if isinstance(data, list):
746+ new_data = {'url': data[0]}
747+ if len(data) == 1:
748+ new_data['branch'] = 'master'
749+ elif len(data) == 2:
750+ new_data['branch'] = data[1]
751+ data = new_data
752+
753+ self._ks = ks
754+ self._owner = owner
755+ self._data = data if data else {}
756+
757+ @property
758+ def owner(self):
759+ return self._owner
760+
761+ # XXX: should this object have a name ?
762+
763+ def __eq__(self, other):
764+ if isinstance(self, other.__class__):
765+ return self.url == other.url and self.branch == other.branch
766+ return False
767+
768+ def __ne__(self, other):
769+ return not self.__eq__(other)
770+
771+ @property
772+ def url(self):
773+ return self._data['url']
774+
775+ @property
776+ def branch(self):
777+ return self._data.get('branch', None)
778+
779+ def __str__(self):
780+ return "{} {}".format(self.url, self.branch)
781+
782+
783+class KernelSnapEntry:
784+ def __init__(self, ks, source, name, data):
785+ self._ks = ks
786+ self._source = source
787+ self._name = name
788+ self._data = data if data else {}
789+
790+ # Convert arches/track to publish-to form.
791+ if 'publish-to' not in self._data:
792+ if 'arches' in self._data:
793+ publish_to = {}
794+ for arch in self._data['arches']:
795+ publish_to[arch] = [self._data.get('track', 'latest')]
796+ self._data['publish-to'] = publish_to
797+
798+ # Convert stable to promote-to form.
799+ if 'promote-to' not in self._data and 'stable' in self._data:
800+ if self._data['stable'] is True:
801+ self._data['promote-to'] = 'stable'
802+ else:
803+ self._data['promote-to'] = 'candidate'
804+ # Assume no promote-to data to mean just to edge.
805+ promote_to = self._data.get('promote-to', 'edge')
806+ if isinstance(promote_to, str):
807+ expand_promote_to = []
808+ for risk in ('edge', 'beta', 'candidate', 'stable'):
809+ expand_promote_to.append(risk)
810+ if risk == promote_to:
811+ break
812+ self._data['promote-to'] = expand_promote_to
813+ # Ensure we have stable when promote-to is present.
814+ if 'promote-to' in self._data and 'stable' not in self._data:
815+ if 'stable' in self._data['promote-to']:
816+ self._data['stable'] = True
817+ else:
818+ self._data['stable'] = False
819+
820+ def __eq__(self, other):
821+ if isinstance(self, other.__class__):
822+ return self.name == other.name and self.source == other.source
823+ return False
824+
825+ def __ne__(self, other):
826+ return not self.__eq__(other)
827+
828+ @property
829+ def series(self):
830+ return self._source.series
831+
832+ @property
833+ def source(self):
834+ return self._source
835+
836+ @property
837+ def name(self):
838+ return self._name
839+
840+ @property
841+ def repo(self):
842+ data = self._data.get('repo', None)
843+ if not data:
844+ return None
845+ return KernelRepoEntry(self._ks, self, data)
846+
847+ @property
848+ def primary(self):
849+ return self._data.get('primary', False)
850+
851+ @property
852+ def gated(self):
853+ return self._data.get('gated', False)
854+
855+ @property
856+ def stable(self):
857+ return self._data.get('stable', False)
858+
859+ @property
860+ def qa(self):
861+ return self._data.get('qa', False)
862+
863+ @property
864+ def hw_cert(self):
865+ return self._data.get('hw-cert', False)
866+
867+ @property
868+ def arches(self):
869+ # XXX: should this be []
870+ return self._data.get('arches', None)
871+
872+ @property
873+ def track(self):
874+ return self._data.get('track', None)
875+
876+ @property
877+ def publish_to(self):
878+ return self._data.get('publish-to', None)
879+
880+ @property
881+ def promote_to(self):
882+ return self._data.get('promote-to', None)
883+
884+ def promote_to_risk(self, risk):
885+ return risk in self._data.get('promote-to', [])
886+
887+ def __str__(self):
888+ return "{} {}".format(str(self.source), self.name)
889+
890+
891+class KernelPackageEntry:
892+ def __init__(self, ks, source, name, data):
893+ self._ks = ks
894+ self._source = source
895+ self._name = name
896+ self._data = data if data else {}
897+
898+ def __eq__(self, other):
899+ if isinstance(self, other.__class__):
900+ return self.name == other.name and self.source == other.source
901+ return False
902+
903+ def __ne__(self, other):
904+ return not self.__eq__(other)
905+
906+ @property
907+ def series(self):
908+ return self._source.series
909+
910+ @property
911+ def source(self):
912+ return self._source
913+
914+ @property
915+ def name(self):
916+ return self._name
917+
918+ @property
919+ def type(self):
920+ return self._data.get('type', None)
921+
922+ @property
923+ def repo(self):
924+ data = self._data.get('repo', None)
925+ if not data:
926+ return None
927+ return KernelRepoEntry(self._ks, self, data)
928+
929+ def __str__(self):
930+ return "{} {} {}".format(str(self.source), self.name, self.type)
931+
932+
933+class KernelSourceEntry:
934+ def __init__(self, ks, series, name, data):
935+ self._ks = ks
936+ self._series = series
937+ self._name = name
938+ self._data = data if data else {}
939+
940+ def __eq__(self, other):
941+ if isinstance(self, other.__class__):
942+ return self.name == other.name and self.series == other.series
943+ return False
944+
945+ def __ne__(self, other):
946+ return not self.__eq__(other)
947+
948+ @property
949+ def name(self):
950+ return self._name
951+
952+ @property
953+ def series(self):
954+ return self._series
955+
956+ @property
957+ def versions(self):
958+ if 'versions' in self._data:
959+ return self._data['versions']
960+
961+ derived_from = self.derived_from
962+ if derived_from is not None:
963+ return derived_from.versions
964+
965+ copy_forward = self.copy_forward
966+ if copy_forward is not None:
967+ return copy_forward.versions
968+
969+ # XXX: should this be []
970+ return None
971+
972+ @property
973+ def version(self):
974+ versions = self.versions
975+ if not versions:
976+ return None
977+ return versions[-1]
978+
979+ @property
980+ def development(self):
981+ return self._data.get('development', self.series.development)
982+
983+ @property
984+ def supported(self):
985+ return self._data.get('supported', self.series.supported)
986+
987+ @property
988+ def severe_only(self):
989+ return self._data.get('severe-only', False)
990+
991+ @property
992+ def stakeholder(self):
993+ return self._data.get('stakeholder', None)
994+
995+ @property
996+ def packages(self):
997+ # XXX: should this return None when empty
998+ result = []
999+ packages = self._data.get('packages')
1000+ if packages:
1001+ for package_key, package in packages.items():
1002+ result.append(KernelPackageEntry(self._ks, self, package_key, package))
1003+ return result
1004+
1005+ def lookup_package(self, package_key):
1006+ packages = self._data.get('packages')
1007+ if not packages or package_key not in packages:
1008+ return None
1009+ return KernelPackageEntry(self._ks, self, package_key, packages[package_key])
1010+
1011+ @property
1012+ def snaps(self):
1013+ # XXX: should this return None when empty
1014+ result = []
1015+ snaps = self._data.get('snaps')
1016+ if snaps:
1017+ for snap_key, snap in snaps.items():
1018+ result.append(KernelSnapEntry(self._ks, self, snap_key, snap))
1019+ return result
1020+
1021+ def lookup_snap(self, snap_key):
1022+ snaps = self._data.get('snaps')
1023+ if not snaps or snap_key not in snaps:
1024+ return None
1025+ return KernelSnapEntry(self._ks, self, snap_key, snaps[snap_key])
1026+
1027+ @property
1028+ def derived_from(self):
1029+ if 'derived-from' not in self._data:
1030+ return None
1031+
1032+ (series_key, source_key) = self._data['derived-from']
1033+
1034+ series = self._ks.lookup_series(series_key)
1035+ source = series.lookup_source(source_key)
1036+
1037+ return source
1038+
1039+ @property
1040+ def testable_flavours(self):
1041+ retval = []
1042+ if (self._data.get('testing') is not None and
1043+ self._data['testing'].get('flavours') is not None
1044+ ):
1045+ for flavour in self._data['testing']['flavours'].keys():
1046+ fdata = self._data['testing']['flavours'][flavour]
1047+ # If we have neither arches nor clouds we represent a noop
1048+ if not fdata:
1049+ continue
1050+ arches = fdata.get('arches', None)
1051+ arches = arches if arches is not None else []
1052+ clouds = fdata.get('clouds', None)
1053+ clouds = clouds if clouds is not None else []
1054+ retval.append(KernelSourceTestingFlavourEntry(flavour, arches, clouds))
1055+ return retval
1056+
1057+ @property
1058+ def invalid_tasks(self):
1059+ retval = self._data.get('invalid-tasks', [])
1060+ if retval is None:
1061+ retval = []
1062+ return retval
1063+
1064+ @property
1065+ def copy_forward(self):
1066+ if 'copy-forward' not in self._data:
1067+ return None
1068+
1069+ # XXX: backwards compatibility.
1070+ if self._data['copy-forward'] is False:
1071+ return None
1072+ if self._data['copy-forward'] is True:
1073+ derived_from = self.derived_from
1074+ if derived_from is None:
1075+ return True
1076+ return self.derived_from
1077+
1078+ (series_key, source_key) = self._data['copy-forward']
1079+
1080+ series = self._ks.lookup_series(series_key)
1081+ source = series.lookup_source(source_key)
1082+
1083+ return source
1084+
1085+ @property
1086+ def backport(self):
1087+ return self._data.get('backport', False)
1088+
1089+ @property
1090+ def routing(self):
1091+ default = 'default'
1092+ if self.series.development:
1093+ default = 'devel'
1094+ if self.series.esm:
1095+ default = 'esm'
1096+ data = self._data.get('routing', default)
1097+ if data is None:
1098+ return data
1099+ return KernelRoutingEntry(self._ks, self, data)
1100+
1101+ @property
1102+ def swm_data(self):
1103+ return self._data.get('swm')
1104+
1105+ @property
1106+ def private(self):
1107+ return self._data.get('private', False)
1108+
1109+ def __str__(self):
1110+ return "{} {}".format(self.series.name, self.name)
1111+
1112+class KernelSourceTestingFlavourEntry:
1113+ def __init__(self, name, arches, clouds):
1114+ self._name = name
1115+ self._arches = arches
1116+ self._clouds = clouds
1117+
1118+ @property
1119+ def name(self):
1120+ return self._name
1121+
1122+ @property
1123+ def arches(self):
1124+ return self._arches
1125+
1126+ @property
1127+ def clouds(self):
1128+ return self._clouds
1129+
1130+class KernelSeriesEntry:
1131+ def __init__(self, ks, name, data, defaults=None):
1132+ self._ks = ks
1133+ self._name = name
1134+ self._data = {}
1135+ if defaults is not None:
1136+ self._data.update(defaults)
1137+ if data is not None:
1138+ self._data.update(data)
1139+
1140+ def __eq__(self, other):
1141+ if isinstance(self, other.__class__):
1142+ return self.name == other.name
1143+ return False
1144+
1145+ def __ne__(self, other):
1146+ return not self.__eq__(other)
1147+
1148+ @property
1149+ def name(self):
1150+ return self._name
1151+
1152+ @property
1153+ def codename(self):
1154+ return self._data.get('codename', None)
1155+
1156+ @property
1157+ def opening(self):
1158+ if 'opening' in self._data:
1159+ if self._data['opening'] is not False:
1160+ return True
1161+ return False
1162+
1163+ def opening_ready(self, *flags):
1164+ if 'opening' not in self._data:
1165+ return True
1166+ allow = self._data['opening']
1167+ if allow is None:
1168+ return False
1169+ if allow in (True, False):
1170+ return not allow
1171+ for flag in flags:
1172+ flag_allow = allow.get(flag, False)
1173+ if flag_allow is None or flag_allow is False:
1174+ return False
1175+ return True
1176+ opening_allow = opening_ready
1177+
1178+ @property
1179+ def development(self):
1180+ return self._data.get('development', False)
1181+
1182+ @property
1183+ def supported(self):
1184+ return self._data.get('supported', False)
1185+
1186+ @property
1187+ def lts(self):
1188+ return self._data.get('lts', False)
1189+
1190+ @property
1191+ def esm(self):
1192+ return self._data.get('esm', False)
1193+
1194+ def __str__(self):
1195+ return "{} ({})".format(self.name, self.codename)
1196+
1197+ @property
1198+ def sources(self):
1199+ result = []
1200+ sources = self._data.get('sources')
1201+ if sources:
1202+ for source_key, source in sources.items():
1203+ result.append(KernelSourceEntry(
1204+ self._ks, self, source_key, source))
1205+ return result
1206+
1207+ @property
1208+ def routing_table(self):
1209+ return self._data.get('routing-table', None)
1210+
1211+ def lookup_source(self, source_key):
1212+ sources = self._data.get('sources')
1213+ if not sources or source_key not in sources:
1214+ return None
1215+ return KernelSourceEntry(self._ks, self, source_key, sources[source_key])
1216+
1217+
1218+# KernelSeries
1219+#
1220+class KernelSeries:
1221+ _url = 'https://git.launchpad.net/~canonical-kernel/' \
1222+ '+git/kteam-tools/plain/info/kernel-series.yaml'
1223+ _url_local = 'file://' + os.path.realpath(os.path.join(os.path.dirname(__file__),
1224+ '..', 'info', 'kernel-series.yaml'))
1225+ #_url = 'file:///home/apw/git2/kteam-tools/info/kernel-series.yaml'
1226+ #_url = 'file:///home/work/kteam-tools/info/kernel-series.yaml'
1227+ _data_txt = {}
1228+
1229+ @classmethod
1230+ def __load_once(cls, url):
1231+ if url not in cls._data_txt:
1232+ response = urlopen(url)
1233+ data = response.read()
1234+ if not isinstance(data, str):
1235+ data = data.decode('utf-8')
1236+ cls._data_txt[url] = data
1237+ return cls._data_txt[url]
1238+
1239+ def __init__(self, url=None, data=None, use_local=os.getenv("USE_LOCAL_KERNEL_SERIES_YAML", False)):
1240+ if data or url:
1241+ if url:
1242+ response = urlopen(url)
1243+ data = response.read()
1244+ if not isinstance(data, str):
1245+ data = data.decode('utf-8')
1246+ else:
1247+ data = self.__load_once(self._url_local if use_local else self._url)
1248+ self._data = yaml.load(data)
1249+
1250+ self._development_series = None
1251+ self._codename_to_series = {}
1252+ for series_key, series in self._data.items():
1253+ if not series:
1254+ continue
1255+ if series.get('development', False):
1256+ self._development_series = series_key
1257+ if 'codename' in series:
1258+ self._codename_to_series[series['codename']] = series_key
1259+
1260+ # Pull out the defaults.
1261+ self._defaults_series = {}
1262+ if 'defaults' in self._data:
1263+ self._defaults_series = self._data['defaults']
1264+ del self._data['defaults']
1265+
1266+ @staticmethod
1267+ def key_series_name(series):
1268+ return [int(x) for x in series.name.split('.')]
1269+
1270+ @property
1271+ def series(self):
1272+ return [KernelSeriesEntry(self, series_key, series,
1273+ defaults=self._defaults_series)
1274+ for series_key, series in self._data.items()]
1275+
1276+ def lookup_series(self, series=None, codename=None, development=False):
1277+ if not series and not codename and not development:
1278+ raise ValueError("series/codename/development required")
1279+ if not series and codename:
1280+ if codename not in self._codename_to_series:
1281+ return None
1282+ series = self._codename_to_series[codename]
1283+ if not series and development:
1284+ if not self._development_series:
1285+ return None
1286+ series = self._development_series
1287+ if series and series not in self._data:
1288+ return None
1289+ return KernelSeriesEntry(self, series, self._data[series],
1290+ defaults=self._defaults_series)
1291+
1292+
1293+if __name__ == '__main__':
1294+ db = KernelSeries()
1295+
1296+ series = db.lookup_series('16.04')
1297+ if series.name != '16.04':
1298+ print('series.name != 16.04')
1299+ if series.codename != 'xenial':
1300+ print('series.codename != xenial')
1301+
1302+ series2 = db.lookup_series(codename='xenial')
1303+ if series2.name != '16.04':
1304+ print('series2.name != 16.04')
1305+ if series2.codename != 'xenial':
1306+ print('series2.codename != xenial')
1307+
1308+ series3 = db.lookup_series(development=True)
1309+ if series3.name != '18.04':
1310+ print('series3.name != 18.04')
1311+ if series3.codename != 'bionic':
1312+ print('series3.codename != bionic')
1313+
1314+ print(str(series), str(series2), str(series3))
1315+
1316+ for series2 in sorted(db.series, key=db.key_series_name):
1317+ print(series2)
1318+
1319+ for source in series.sources:
1320+ print(str(source), source.series.name, source.name)
1321+
1322+ print(source.derived_from)
1323+ print(source.versions)
1324+
1325+ for package in source.packages:
1326+ print("PACKAGE", str(package))
1327+
1328+ for snap in source.snaps:
1329+ print("SNAP", str(snap), snap.arches)
1330+
1331+
1332+# vi:set ts=4 sw=4 expandtab:
1333
1334=== modified file 'sru-release'
1335--- sru-release 2019-08-27 11:30:27 +0000
1336+++ sru-release 2019-09-19 13:51:06 +0000
1337@@ -39,6 +39,8 @@
1338
1339 from launchpadlib.launchpad import Launchpad
1340
1341+from kernel_series import KernelSeries
1342+
1343
1344 # Each entry in this list is a list of source packages that are known
1345 # to have inter-dependencies and must be released simultaneously.
1346@@ -179,10 +181,10 @@
1347 'published': proposed_date}
1348 '''
1349 versions = defaultdict(dict)
1350- if options.esm:
1351+ if src_archive.reference == 'ubuntu':
1352+ pocket = 'Proposed'
1353+ else:
1354 pocket = 'Release'
1355- else:
1356- pocket = 'Proposed'
1357
1358 matches = src_archive.getPublishedSources(
1359 source_name=sourcename, exact_match=not options.pattern,
1360@@ -193,9 +195,9 @@
1361 source_name=match.source_package_name, exact_match=True,
1362 status='Published', distro_series=series):
1363 key = pub.pocket.lower()
1364- # special case for ESM ppas, which don't have pockets but need
1365+ # special case for ppas, which don't have pockets but need
1366 # to be treated as -proposed
1367- if options.esm and key == 'release':
1368+ if pocket == 'Release' and key == 'release':
1369 key = 'proposed'
1370 versions[pub.source_package_name][key] = (
1371 pub.source_package_version)
1372@@ -213,7 +215,6 @@
1373 if pocket in pub.pocket:
1374 versions[pub.source_package_name]['changesfile'] = (
1375 pub.changesFileUrl())
1376-
1377 # devel version
1378 if devel_series:
1379 for pub in src_archive.getPublishedSources(
1380@@ -360,6 +361,13 @@
1381 release = args.pop(0)
1382 packages = args
1383
1384+ # XXX: we only want to instantiate KernelSeries if we suspect this is
1385+ # a kernel package, this is necessarily dirty, dirty, dirty.
1386+ kernel_checks = False
1387+ for package in packages:
1388+ if package.startswith('linux-') or package == 'linux':
1389+ kernel_checks = True
1390+
1391 if not options.skip_package_group_check:
1392 try:
1393 packages = check_package_sets(packages)
1394@@ -376,12 +384,57 @@
1395 sys.stderr.write(
1396 'WARNING: No current development series, -d will not work\n')
1397 devel_series = None
1398- if release in ('precise', 'trusty'):
1399+
1400+ ks_source = None
1401+ if kernel_checks:
1402+ kernel_series = KernelSeries()
1403+
1404+ # See if we have a kernel-series record for this package. If we do
1405+ # then we are going to pivot to the routing therein.
1406+ ks_series = kernel_series.lookup_series(codename=release)
1407+ for ks_source_find in ks_series.sources:
1408+ for ks_package in ks_source_find.packages:
1409+ if ks_package.name == packages[0]:
1410+ ks_source = ks_source_find
1411+ break
1412+
1413+ # First confirm everything in this set we are attempting to release
1414+ # are indeed listed as valid for this kernel.
1415+ if ks_source is not None:
1416+ for package in packages:
1417+ if ks_source.lookup_package(package) is None:
1418+ sys.stderr.write(
1419+ 'WARNING: {} not found in packages for kernel {}\n'.format(
1420+ package, ks_source.name))
1421+
1422+ if ks_source is None and release in ('precise', 'trusty'):
1423 sys.stdout.write(
1424 'Called for {}; assuming kernel ESM publication\n'.format(release))
1425 options.esm = True
1426
1427- if options.esm:
1428+ # If we found a KernelSeries entry this has accurate routing information
1429+ # attached use that.
1430+ if ks_source is not None:
1431+ src_archive_ref, src_archive_pocket = ks_source.routing.lookup_destination('proposed', primary=True)
1432+ src_archive = launchpad.archives.getByReference(
1433+ reference=src_archive_ref)
1434+ dst_archive_ref, dst_archive_pocket = ks_source.routing.lookup_destination('updates', primary=True)
1435+ if dst_archive_ref == src_archive_ref:
1436+ dst_archive = src_archive
1437+ else:
1438+ dst_archive = launchpad.archives.getByReference(
1439+ reference=dst_archive_ref)
1440+
1441+ # Announce any non-standard archive routing.
1442+ if src_archive_ref != 'ubuntu':
1443+ print("Src Archive: {}".format(src_archive_ref))
1444+ if dst_archive_ref != 'ubuntu':
1445+ print("Dst Archive: {}".format(dst_archive_ref))
1446+ # --security is meaningless for private PPA publishing (XXX: currently true)
1447+ options.security = False
1448+ options.release = True
1449+
1450+ elif options.esm:
1451 # --security is meaningless for ESM everything is a security update.
1452 options.security = False
1453 options.release = True

Subscribers

People subscribed via source and target branches