Merge lp:~fginther/auto-package-testing/remove-boottestjob into lp:~canonical-ci-engineering/auto-package-testing/add-boottest-requests

Proposed by Francis Ginther
Status: Merged
Merged at revision: 390
Proposed branch: lp:~fginther/auto-package-testing/remove-boottestjob
Merge into: lp:~canonical-ci-engineering/auto-package-testing/add-boottest-requests
Diff against target: 541 lines (+40/-461)
3 files modified
jenkins/adtjob.py (+33/-26)
jenkins/boottest-britney (+7/-5)
jenkins/boottestjob.py (+0/-430)
To merge this branch: bzr merge lp:~fginther/auto-package-testing/remove-boottestjob
Reviewer Review Type Date Requested Status
Canonical CI Engineering Pending
Review via email: mp+248047@code.launchpad.net

Commit message

Remove boottestjob.py, replacing differences with the boottest flag.

Description of the change

Remove boottestjob.py, replacing differences with the boottest flag.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'jenkins/adtjob.py'
2--- jenkins/adtjob.py 2015-01-22 19:12:09 +0000
3+++ jenkins/adtjob.py 2015-01-29 20:22:15 +0000
4@@ -219,32 +219,39 @@
5 causes[self.pkgname] = version
6 ret = True
7
8- # Verification of the dependencies
9- # Run a test if:
10- # - No package list is specified and
11- # - this is a new dependency
12- # - or version of a dependency in the archive is newer than previous run
13- # - Or if a package list is specified the following conditions must
14- # also be met:
15- # - and the dependency is in the list
16- # - the version in the archive is newer or equal to the version requested
17- #
18- # Note that request files use source package names while dependency
19- # lists returned by get_package_info use binary package names
20- for depname, depver in depends.iteritems():
21- depsrcname = self._get_sourcename(depname)
22- if (not pkglist or
23- (pkglist and depsrcname in pkglist.keys() and
24- apt_pkg.version_compare(pkglist[depsrcname]['version'], depver) <= 0)):
25- if not depname in self.depends:
26- logging.info("== New dependency '%s'.", depname)
27- causes[depsrcname] = depver
28- ret = True
29- elif apt_pkg.version_compare(self.depends[depname], depver) < 0:
30- logging.info("== New version of dependency '%s %s'.",
31- depname, depver)
32- causes[depsrcname] = depver
33- ret = True
34+ if self.boottest:
35+ # Verification of the dependencies
36+ # Run a test if:
37+ # - No package list is specified and
38+ # - this is a new dependency
39+ # - or version of a dependency in the archive is newer than previous run
40+ # - Or if a package list is specified the following conditions must
41+ # also be met:
42+ # - and the dependency is in the list
43+ # - the version in the archive is newer or equal to the version requested
44+ #
45+ # Note that request files use source package names while dependency
46+ # lists returned by get_package_info use binary package names
47+ # XXX - fginther 20150129
48+ # Skip this for boottesting because the reverse dependences
49+ # will almost always already be a part of the image being tested
50+ # The only exception is when the reverse dependence is also in
51+ # the proposed pocket. We'll just acknowledge that these are
52+ # skipped for now.
53+ for depname, depver in depends.iteritems():
54+ depsrcname = self._get_sourcename(depname)
55+ if (not pkglist or
56+ (pkglist and depsrcname in pkglist.keys() and
57+ apt_pkg.version_compare(pkglist[depsrcname]['version'], depver) <= 0)):
58+ if not depname in self.depends:
59+ logging.info("== New dependency '%s'.", depname)
60+ causes[depsrcname] = depver
61+ ret = True
62+ elif apt_pkg.version_compare(self.depends[depname], depver) < 0:
63+ logging.info("== New version of dependency '%s %s'.",
64+ depname, depver)
65+ causes[depsrcname] = depver
66+ ret = True
67
68 if ret:
69 self.causes = causes
70
71=== modified file 'jenkins/boottest-britney'
72--- jenkins/boottest-britney 2015-01-29 01:55:34 +0000
73+++ jenkins/boottest-britney 2015-01-29 20:22:15 +0000
74@@ -27,7 +27,7 @@
75 import subprocess
76 import yaml
77 import argparse
78-from boottestjob import BoottestJob
79+from adtjob import AdtJob
80 from aptcache import AptCache
81 from time import strftime
82 from shutil import copyfile
83@@ -321,8 +321,9 @@
84 deps_path = self.status_pattern % (
85 self.config['release'], self.pocket,
86 self.config['arch'], pkgname)
87- job = BoottestJob(self.cache, deps_path,
88- use_proposed=self.args.use_proposed)
89+ job = AdtJob(self.cache, deps_path,
90+ use_proposed=self.args.use_proposed,
91+ boottest=True)
92 if not job.package:
93 job.release = self.config['release']
94 job.pkgname = pkgname
95@@ -368,8 +369,9 @@
96 deps_path = self.status_pattern % (
97 self.config['release'], self.pocket,
98 self.config['arch'], pkgname)
99- job = BoottestJob(self.cache, deps_path,
100- use_proposed=self.args.use_proposed)
101+ job = AdtJob(self.cache, deps_path,
102+ use_proposed=self.args.use_proposed,
103+ boottest=True)
104 if ('causes' in pkgprops and
105 pkgname in pkgprops['causes'] and
106 pkgprops['causes'][pkgname] is None and job.version):
107
108=== removed file 'jenkins/boottestjob.py'
109--- jenkins/boottestjob.py 2015-01-29 01:55:34 +0000
110+++ jenkins/boottestjob.py 1970-01-01 00:00:00 +0000
111@@ -1,430 +0,0 @@
112-#! /usr/bin/python
113-""" Interface between britney and autopkgtest running on Jenkins
114-
115-"""
116-# Copyright (C) 2012, Canonical Ltd (http://www.canonical.com/)
117-#
118-# Author: Jean-Baptiste Lallement <jean-baptiste.lallement@canonical.com>
119-#
120-# This software is free software: you can redistribute it
121-# and/or modify it under the terms of the GNU General Public License
122-# as published by the Free Software Foundation, either version 3 of
123-# the License, or (at your option) any later version.
124-#
125-# This software is distributed in the hope that it will
126-# be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
127-# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
128-# GNU General Public License for more details.
129-#
130-# You should have received a copy of the GNU General Public License
131-# along with this software. If not, see <http://www.gnu.org/licenses/>.
132-#
133-import logging
134-import os
135-import sys
136-import json
137-import apt_pkg
138-import subprocess
139-import yaml
140-from xml.sax.saxutils import escape
141-
142-if os.path.exists(os.path.join(os.path.dirname(__file__), "../.bzr")):
143- sys.path.insert(0, os.path.dirname(__file__))
144-from adtnotify import AdtNotify
145-
146-try:
147- import jenkins
148-except ImportError:
149- sys.stderr.write('python-jenkins is not installed. Jenkins support is '
150- 'disabled\n')
151-try:
152- import jinja2
153-except ImportError:
154- sys.stderr.write('python-jinja2 is not installed. Jinja support is '
155- 'disabled\n')
156-from urllib2 import urlopen
157-
158-
159-ARCHS = ('i386', 'amd64', 'all')
160-BINDIR = os.path.dirname(__file__)
161-JENKINS_TMPL = 'jenkins_config.xml.tmpl'
162-JENKINS_TMPL_ARMHF = 'jenkins_config_armhf.xml.tmpl'
163-JENKINS_TMPL_PPC64EL = 'jenkins_config_ppc64el.xml.tmpl'
164-DEFAULT_RECIPIENTS = ["ubuntu-testing-notifications@lists.ubuntu.com",
165- "jean-baptiste.lallement@canonical.com",
166- "martin.pitt@ubuntu.com"]
167-
168-
169-class BoottestJob(object):
170- '''Class that manages jobs'''
171- def __init__(self, cache, state_path, use_proposed=False, credfile=None):
172- global ARCHS
173-
174- self.job_status = {
175- 'status': dict([(arch, None) for arch in ARCHS]),
176- 'release': None,
177- 'package': None,
178- 'version': None,
179- 'depends': {}, # package:version
180- 'causes': {}, # package:version
181- }
182-
183- self.state_path = state_path
184- self.cache = cache
185-
186- self.status = dict([(arch, None) for arch in ARCHS])
187- self.release = None
188- self.package = None
189- self.pkgname = None
190- self.version = None
191- self.depends = {}
192- self.causes = {}
193-
194- self.use_proposed = use_proposed
195- self.load_status(state_path)
196-
197- if credfile:
198- self.credentials = self.load_jenkins_credentials(credfile)
199- if self.credentials:
200- # "http://10.189.74.2:8080/job/%s%s-adt-%s/build?token=TOKEN"
201- self.jenkins_url = "%s/job/%%s/build?token=%s" \
202- % (self.credentials['url'], self.credentials['token'])
203-
204- def load_jenkins_credentials(self, path):
205- """ Load Credentials from credentials configuration file """
206- if not os.path.exists(path):
207- return False
208-
209- logging.debug('Loading credentials from %s', path)
210- cred = yaml.load(file(path, 'r'))
211- return False if not 'jenkins' in cred else cred['jenkins']
212-
213- def load_status(self, state_path):
214- ''' Loads status of a job from configuration file'''
215- logging.debug("Config path: '%s'", state_path)
216- if not os.path.exists(state_path):
217- logging.debug("'%s' doesn't exist", state_path)
218- else:
219- logging.debug("Loading current status")
220- with open(state_path, 'r') as fp:
221- self.job_status = json.load(fp)
222-
223- self.status = self.job_status['status']
224- self.release = self.job_status['release']
225- self.pkgname = self.job_status['package']
226- self.package = apt_pkg.SourceRecords()
227- self.package.restart()
228- if not self.package.lookup(self.pkgname):
229- logging.warning('No source package named: %s' % self.pkgname)
230- self.version = self.job_status['version']
231- self.depends = self.job_status['depends']
232- if 'causes' in self.job_status:
233- self.causes = self.job_status['causes']
234-
235- def write_status(self):
236- '''Write status to a file'''
237- with open(self.state_path, 'w') as fp:
238- logging.debug("Writing status file to '%s'", self.state_path)
239- self.job_status = {
240- 'status': self.status,
241- 'release': self.release,
242- 'package': self.pkgname,
243- 'version': self.version,
244- 'depends': self.depends,
245- 'causes': self.causes,
246- }
247- json.dump(self.job_status, fp)
248-
249- def update_status(self):
250- '''Update package information'''
251- logging.debug('Updating job status')
252- if not self.status:
253- self.status = dict([(arch, 'NEEDRUN') for arch in ARCHS])
254-
255- (self.version, pocket, self.depends) = self.get_package_info()
256-
257- def get_package_info(self):
258- '''Get package info for the newest version available in the
259- repository from apt
260-
261- :return: version, pocket, binary dependencies
262- '''
263- pkg = apt_pkg.SourceRecords()
264- pkg.restart()
265-
266- pocket = None
267- version = None
268- depends = None
269-
270- while pkg.lookup(self.pkgname):
271- if not version or apt_pkg.version_compare(version, pkg.version) < 0:
272- pocket = pkg.index.describe.split(' ', 4)[1]
273- version = pkg.version
274- binaries = pkg.binaries
275-
276- if version:
277- depends = self.binaries_depends(binaries)
278- return (version, pocket, depends)
279-
280- def run_required(self, pkglist={}):
281- '''Return True if the job must run
282-
283- Conditions are:
284- - No version for the package (never ran before)
285- - Version of the package changed
286- - Version of a dependency changed
287-
288- @pkglist: If specified the conditions to run a job are:
289- - package is in the list and no test has been run before (no
290- version)
291- - package is in the list and the version is higher than previous run
292- - package is not in the list but a dependency is and the version is
293- higher than previous run or package has never been tested
294- '''
295- logging.info('Checking status for: %s %s', self.release, self.pkgname)
296- # Find the newest package in enabled repositories
297- (version, pocket, depends) = self.get_package_info()
298-
299- if not version:
300- logging.warning("Source package not found: %s", self.pkgname)
301- return False
302-
303- logging.debug('Found package version: %s %s', version, pocket)
304-
305- # Never ran the test before
306- # If a package list is specified the package must be in the list
307- if (not self.version and
308- (not pkglist or
309- (pkglist and self.pkgname in pkglist.keys()))):
310- logging.info("== New package '%s %s'", self.pkgname, version)
311- self.causes[self.pkgname] = version
312- return True
313-
314- # New version of the package
315- # Trigger a test if the version in the archive is newer than last run
316- # Or if a package list is specified, the version in the archive must be
317- # new or equal to the version requested which must be newer than the
318- # last version of the package that have been tested
319- ret = False
320- causes = {}
321-
322- if (self.version is not None and apt_pkg.version_compare(self.version, version) < 0 and
323- (not pkglist or
324- (pkglist and self.pkgname in pkglist.keys() and
325- apt_pkg.version_compare(pkglist[self.pkgname]['version'], version) <= 0))):
326- logging.info("== New version of package '%s %s'",
327- self.pkgname, version)
328- causes[self.pkgname] = version
329- ret = True
330-
331- if ret:
332- self.causes = causes
333- else:
334- logging.info('Same version. Skipped')
335- return ret
336-
337- def _get_sourcename(self, pkgname):
338- '''Returns a source package name
339-
340- :param pkgname: Package name which we want to find source name'''
341-
342- try:
343- pkg = self.cache[pkgname]
344- return pkg.candidate.source_name
345- except:
346- logging.error('Unexpected error: %s', sys.exc_info()[0])
347- return pkgname
348-
349- def submit(self, dest, force=False):
350- '''Run a job is needed
351-
352- :param force: Force run even if there is no dependency update
353- '''
354- if force or self.run_required():
355- logging.debug('Starting job')
356- self.status = dict([(arch, 'RUNNING') for arch in ARCHS])
357- self.update_status()
358- self.write_status()
359- logging.info('Triggering remote job for package %s', self.pkgname)
360- cmd = ['rsync', '-a', self.state_path, dest]
361- logging.debug('Running %s', cmd)
362- try:
363- subprocess.check_call(cmd)
364- except subprocess.CalledProcessError as exc:
365- logging.error('Command failed: %s', exc)
366- return False
367- return True
368-
369- def binaries_depends(self, binaries):
370- '''
371- Return a dict with the list of dependencies for all the binary
372- packages built from this source
373- '''
374- depends = {}
375- if not self.pkgname:
376- return None
377-
378- virtpkgs = []
379- for pkgname in binaries:
380- try:
381- pkgrec = self.cache[pkgname]
382- for dep in pkgrec.candidate.dependencies:
383- for basedep in dep.or_dependencies:
384- try:
385- if self.cache.is_virtual_package(basedep.name):
386- virtpkgs.append(basedep)
387- else:
388- basedeprec = self.cache[basedep.name]
389- depends[basedeprec.name] = \
390- basedeprec.candidate.version
391- except KeyError, exc:
392- logging.warning(exc)
393-
394- except KeyError, exc:
395- logging.warning(exc)
396-
397- # Process virtual packages
398- # providing packages are only added if there is only 1 package providing
399- # this virtual dependency (for dependency on a version of an ABI) and
400- # if it is not already in the list of binary dependencies
401- deps = set(depends.keys())
402- for vpkg in virtpkgs:
403- ppkgs = self.cache.get_providing_packages(vpkg.name)
404- if (len(ppkgs) > 1):
405- continue
406- vdeps = set([bpkg.name for bpkg in ppkgs])
407- if not deps.intersection(vdeps):
408- # No providing package is in the list of dependencies
409- logging.debug("Adding providing package to list of dependencies: '%s'", vdeps)
410- for bpkg in ppkgs:
411- depends[bpkg.name] = bpkg.candidate.version
412- logging.debug('Dependency list: %s', depends)
413- return depends
414-
415- def execute(self, update=False, dryrun=False):
416- '''Execute a jenkins job'''
417- logging.debug('Executing job')
418-
419- # Creates job from template if it doesn't exist
420- # or update it if it already exists
421- # Executes it
422- if not os.path.exists(os.path.join(BINDIR, JENKINS_TMPL)):
423- logging.warning('Template file doesn\'t exist: %s', JENKINS_TMPL)
424- return False
425-
426- templates = jinja2.Environment(loader=jinja2.FileSystemLoader(BINDIR))
427-
428- opts = '' # Args to pass to run-adt-test
429- jobname = self.release
430- if self.use_proposed:
431- opts += '-P'
432- jobname += '-adt-' + self.pkgname.replace('+', '-')
433- jobname_armhf = jobname + '-armhf'
434- jobname_ppc64el = jobname + '-ppc64el'
435-
436- # Who should be notified
437- notify = AdtNotify(package=self.pkgname, release=self.release)
438- notify.collect()
439- # Space separated list of recipients
440- email_recipients = ",".join(DEFAULT_RECIPIENTS)
441- email_subject = "$DEFAULT_SUBJECT"
442- email_content = "$DEFAULT_CONTENT\n\n"
443-
444- for notification in notify.notifications:
445- email_content += "%s %s uploaded on %s by %s <%s>\n" % notification
446- if notification[4] is not None:
447- email_recipients += ',%s' % notification[4]
448-
449- ctxt = {'release': self.release,
450- 'release_url': "",
451- 'test': self.pkgname,
452- 'opts': opts,
453- 'jobname': jobname,
454- 'email_recipients': email_recipients,
455- 'email_subject': escape(email_subject),
456- 'email_content': escape(email_content)
457- }
458-
459- logging.debug('Generating job: %s', jobname)
460- tmpl = templates.get_template(JENKINS_TMPL)
461- tmpl_armhf = templates.get_template(JENKINS_TMPL_ARMHF)
462- tmpl_ppc64el = templates.get_template(JENKINS_TMPL_PPC64EL)
463- config = tmpl.render(ctxt)
464- config_armhf = tmpl_armhf.render(ctxt)
465- config_ppc64el = tmpl_ppc64el.render(ctxt)
466- self.__update_jenkins_job(jobname_armhf, config_armhf, update, dryrun)
467- self.__update_jenkins_job(jobname_ppc64el, config_ppc64el, update, dryrun)
468- if not self.__update_jenkins_job(jobname, config, update, dryrun):
469- return False
470-
471- job_url = self.jenkins_url % jobname
472- logging.debug('Job URL: %s', job_url)
473- if not dryrun:
474- urlopen(job_url)
475- else:
476- logging.warning('dry run enabled, job won\'t be executed')
477- return True
478-
479- def __update_jenkins_job(self, jobname, jobconfig, update=False,
480- dryrun=False):
481- """Update a jenkins job"""
482- settings = self.credentials
483- if not settings['url']:
484- logging.error("Please provide a URL to the jenkins instance.")
485- sys.exit(1)
486-
487- if 'username' in settings:
488- logging.debug('Logging to jenkins with user %s',
489- settings['username'])
490- jkh = jenkins.Jenkins(settings['url'],
491- username=settings['username'],
492- password=settings['password'])
493- else:
494- logging.debug('Logging to jenkins anonymously')
495- jkh = jenkins.Jenkins(settings['url'])
496- if not jkh.job_exists(jobname):
497- logging.info("Creating Jenkins Job %s ", jobname)
498- if dryrun:
499- logging.warning('dry run enabled, job won\'t be created')
500- else:
501- jkh.create_job(jobname, jobconfig)
502- else:
503- # Do not requeue or reconfigure running/queued jobs
504- try:
505- job = jkh.get_job_info(jobname)
506- if job['inQueue']:
507- logging.debug("Job '%s' already queued. skipped!", jobname)
508- return False
509- elif job['color'].endswith('_anime'):
510- logging.debug("Job '%s' already running. skipped!", jobname)
511- return False
512- except jenkins.JenkinsException as exc:
513- logging.warning('get_job_info failed with exception: %s', exc)
514- return False
515- if update:
516- logging.info("Reconfiguring Jenkins Job %s ", jobname)
517- if dryrun:
518- logging.warning('dry run enabled, job won\'t be '
519- 'updated')
520- else:
521- jkh.reconfig_job(jobname, jobconfig)
522- else:
523- logging.debug('update set to %s. Skipping reconfiguration '
524- 'of %s', update, jobname)
525- return True
526-
527- def is_newer_dependency(self, pkgname, pkgversion):
528- ''' Returns true if package in argument is newer than one of the
529- dependency
530- '''
531- if not self.depends:
532- return True
533-
534- for depname, depversion in self.depends.iteritems():
535- srcname = self._get_sourcename(depname)
536- if srcname == pkgname:
537- if apt_pkg.version_compare(depversion, pkgversion) < 0:
538- logging.debug('New dependency')
539- return True
540-
541- return False

Subscribers

People subscribed via source and target branches