launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #01149
[Merge] lp:~jml/launchpad/buildd-slavescanner-bustage into lp:launchpad/devel
Jonathan Lange has proposed merging lp:~jml/launchpad/buildd-slavescanner-bustage into lp:launchpad/devel.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
This branch takes the tests in buildd-slavescanner.txt that were exercising the BinaryPackageBuildBehavior class and turns them into more-focused Python tests.
Some of the tests were moved to archive-dependencies.txt, since they were really testing something at a much lower level.
To do this we had to update the mocks that we use and tweak the factory a little.
There are a few incidental cleanups & comments in the actual code, but nothing that puts the massive test refactoring at risk.
--
https://code.launchpad.net/~jml/launchpad/buildd-slavescanner-bustage/+merge/36187
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jml/launchpad/buildd-slavescanner-bustage into lp:launchpad/devel.
=== modified file 'lib/lp/buildmaster/model/builder.py'
--- lib/lp/buildmaster/model/builder.py 2010-09-21 09:05:34 +0000
+++ lib/lp/buildmaster/model/builder.py 2010-09-21 18:41:18 +0000
@@ -115,6 +115,11 @@
class BuilderSlave(object):
"""Add in a few useful methods for the XMLRPC slave."""
+ # WARNING: If you change the API for this, you should also change the APIs
+ # of the mocks in soyuzbuilderhelpers to match. Otherwise, you will have
+ # many false positives in your test run and will most likely break
+ # production.
+
# XXX: This (BuilderSlave) should use composition, rather than
# inheritance.
=== modified file 'lib/lp/soyuz/doc/archive-dependencies.txt'
--- lib/lp/soyuz/doc/archive-dependencies.txt 2010-08-24 15:29:01 +0000
+++ lib/lp/soyuz/doc/archive-dependencies.txt 2010-09-21 18:41:18 +0000
@@ -250,18 +250,28 @@
deb http://ftpmaster.internal/ubuntu hoary-updates
main restricted universe multiverse
-Similarly, populated PPA dependencies are listed in the building
+Similarly, unpopulated PPA dependencies are *not* listed in the building
'sources_list'.
>>> mark = getUtility(IPersonSet).getByName('mark')
+ >>> archive_dependency = cprov.archive.addArchiveDependency(
+ ... mark.archive, PackagePublishingPocket.RELEASE,
+ ... getUtility(IComponentSet)['main'])
+ >>> print_building_sources_list(a_build)
+ deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
+ deb http://ftpmaster.internal/ubuntu hoary
+ main restricted universe multiverse
+ deb http://ftpmaster.internal/ubuntu hoary-security
+ main restricted universe multiverse
+ deb http://ftpmaster.internal/ubuntu hoary-updates
+ main restricted universe multiverse
+
+Similarly, but *populated* PPA dependencies *are* listed in the building
+'sources_list'.
+
>>> pub_binaries = test_publisher.getPubBinaries(
... binaryname='dep-bin', archive=mark.archive,
... status=PackagePublishingStatus.PUBLISHED)
-
- >>> archive_dependency = cprov.archive.addArchiveDependency(
- ... mark.archive, PackagePublishingPocket.RELEASE,
- ... getUtility(IComponentSet)['main'])
-
>>> print_building_sources_list(a_build)
deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
deb http://ppa.launchpad.dev/mark/ppa/ubuntu hoary main
=== modified file 'lib/lp/soyuz/doc/buildd-slave.txt'
--- lib/lp/soyuz/doc/buildd-slave.txt 2010-04-30 10:00:34 +0000
+++ lib/lp/soyuz/doc/buildd-slave.txt 2010-09-21 18:41:18 +0000
@@ -13,43 +13,6 @@
>>> from canonical.buildd.tests import BuilddSlaveTestSetup
>>> BuilddSlaveTestSetup().setUp()
-Use simple xmlrpclib client to certify the BuildSlave is running
-
- >>> import xmlrpclib
- >>> slave = xmlrpclib.Server('http://localhost:8221/rpc/')
- >>> slave.echo('Hello World')
- ['Hello World']
-
-With slave protocol v1.0new, the only way to get files to the slave is to
-put them in the librarian first...
-
- >>> from canonical.librarian.client import LibrarianClient
- >>> from StringIO import StringIO
- >>> from canonical.launchpad.database import LibraryFileAlias
- >>> import transaction
- >>> lc = LibrarianClient()
- >>> helloworld = "Hello World"
- >>> hw_sio = StringIO(helloworld)
- >>> alias = lc.addFile("HelloWorld.txt", len(helloworld),
- ... hw_sio, "text/plain")
- >>> transaction.commit()
- >>> lf = LibraryFileAlias.get(alias)
- >>> present, info = slave.ensurepresent(
- ... lf.content.sha1, lf.http_url, "", "")
- >>> present, info
- (True, 'Download')
-
-As of slave protocol v1.0new, /filecache/SHA1SUM is *THE* way
-to retrieve files from the slave. Verify it works...
-
- >>> from urllib2 import urlopen
- >>> f = urlopen("http://localhost:8221/filecache/" + lf.content.sha1)
- >>> hw_str = f.read()
- >>> f.close()
- >>> hw_str == helloworld
- True
-
-
== BuilderSet polling operations ==
>>> import logging
@@ -90,6 +53,7 @@
At this point the buildd-slave is not accessible anymore.
+ >>> import xmlrpclib
>>> s = xmlrpclib.Server('http://localhost:8221/rpc/')
>>> s.info()
Traceback (most recent call last):
=== modified file 'lib/lp/soyuz/doc/buildd-slavescanner.txt'
--- lib/lp/soyuz/doc/buildd-slavescanner.txt 2010-09-16 14:36:47 +0000
+++ lib/lp/soyuz/doc/buildd-slavescanner.txt 2010-09-21 18:41:18 +0000
@@ -112,12 +112,10 @@
Make sure that a_builder has no active builds:
- >>> from canonical.launchpad.ftests import syncUpdate
>>> if a_builder.currentjob is not None:
... currentjob = a_builder.currentjob
... currentjob.setDateStarted(None)
... currentjob.builder = None
- ... syncUpdate(currentjob)
Force the test builder to be 'ok' as the code required to do this
automatically is not yet factored into the content class.
@@ -595,11 +593,9 @@
>>> resurrect_build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(
... current_job)
>>> resurrect_build.status = BuildStatus.NEEDSBUILD
- >>> syncUpdate(resurrect_build)
>>> current_job.builder = None
>>> current_job.setDateStarted(None)
>>> current_job.lastscore = 0
- >>> syncUpdate(current_job)
IBuilder.findCandidate also identifies if there are builds for
superseded source package releases in the queue and marks the
@@ -740,66 +736,6 @@
>>> commit()
>>> LaunchpadZopelessLayer.switchDbUser(config.builddmaster.dbuser)
-For building a candidate in the release pocket for the main component
-and the primary archive It will pass an 'archives' argument to the
-slave that contains sources.list entries for each pocket required in
-the primary archive dependency tree.
-
-We also pass arguments called 'suite' which is the current distroseries and
-pocket, (e.g. edgy-updates) and 'archive_purpose' which contains the build's
-archive.purpose (e.g. PRIMARY or PPA). These latter two arguments are
-used in the chroot to determine whether it needs to turn on some features
-or not (like pkgstriptranslations and pkgmaintainermangler).
-
-Please note also that the 'archive_private' flag is passed to the slave
-builder. It is True for private archives and False otherwise.
-
- >>> a_builder.setSlaveForTesting(OkSlave())
- >>> a_builder.is_available
- True
- >>> candidate = a_build.queueBuild()
- >>> removeSecurityProxy(a_builder)._dispatchBuildCandidate(candidate)
- ensurepresent called, url=...
- ensurepresent called,
- url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
- OkSlave BUILDING
- Archives:
- deb http://ftpmaster.internal/ubuntu hoary main
- Suite: hoary
- Ogre-component: main
- Archive Purpose: PRIMARY
- Archive Private: False
-
- >>> candidate.destroySelf()
-
-Currently we can theoretically dispatch a build candidate for a
-builder in 'manual' mode.
-
-Although this will not be optimal, because we can only
-do it once the manual builder has been collected (due to the
-BuildQueue.builder constraint). Also because we don't yet provide a
-API/UI method to request the dispatch in advance.
-
- >>> a_builder.manual = True
- >>> commit()
- >>> a_builder.setSlaveForTesting(OkSlave())
- >>> a_builder.is_available
- True
- >>> candidate = a_build.queueBuild()
- >>> removeSecurityProxy(a_builder)._dispatchBuildCandidate(candidate)
- ensurepresent called, url=...
- ensurepresent called,
- url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
- OkSlave BUILDING
- Archives:
- deb http://ftpmaster.internal/ubuntu hoary main
- Suite: hoary
- Ogre-component: main
- Archive Purpose: PRIMARY
- Archive Private: False
-
- >>> candidate.destroySelf()
-
Partner archive builds will set up the 'archives' argument such that it
references all the required pockets/components in the primary archive, in
addition to a reference to the release pocket in the partner archive itself.
@@ -812,10 +748,6 @@
>>> a_builder.is_available
True
- >>> candidate = a_build.queueBuild()
- >>> setupBuildQueue(candidate, a_builder)
- >>> last_stub_mail_count = len(stub.test_emails)
-
The partner archive won't be passed to the builder unless it has at
least one published binary availble in the target distroarchseries.
This feature fixes bug #196782, when archive/suites got passed to
@@ -823,229 +755,12 @@
any PPA/suite will fail during the first 20 minutes because no empty
indexes are published.
-Note that only a published binary in the right context will make the
-archive relevant, anything PENDING or published in another context
-wouldn't work.
-
- >>> warty = getUtility(IDistributionSet)['ubuntu']['warty']
- >>> create_binary_publication_for(
- ... partner_archive, warty, PackagePublishingStatus.PUBLISHED)
-
- >>> hoary = getUtility(IDistributionSet)['ubuntu']['hoary']
- >>> create_binary_publication_for(
- ... partner_archive, hoary, PackagePublishingStatus.PENDING)
-
-So, at moment, partner archive is still not relevant for builds in
-hoary/i386. It's not passed to the builder.
-
- >>> removeSecurityProxy(a_builder)._dispatchBuildCandidate(candidate)
- ensurepresent called, url=...
- ensurepresent called,
- url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
- OkSlave BUILDING
- Archives:
- deb http://ftpmaster.internal/ubuntu hoary main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-security main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-updates main restricted universe multiverse
- Suite: hoary
- Ogre-component: main
- Archive Purpose: PARTNER
- Archive Private: False
-
-Let's try it again.
-
- >>> candidate.destroySelf()
- >>> a_builder.setSlaveForTesting(OkSlave())
- >>> a_builder.is_available
- True
-
- >>> candidate = a_build.queueBuild()
- >>> setupBuildQueue(candidate, a_builder)
- >>> last_stub_mail_count = len(stub.test_emails)
-
- >>> removeSecurityProxy(a_build).archive = ubuntu.main_archive
- >>> candidate.destroySelf()
-
-But this time We will create a valid publication on partner hoary/i386.
-
- >>> from lp.soyuz.interfaces.component import IComponentSet
- >>> commit()
- >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
- >>> login('foo.bar@xxxxxxxxxxxxx')
- >>> pub_source = test_publisher.getPubSource(
- ... archive=partner_archive, distroseries=hoary,
- ... status=PackagePublishingStatus.PUBLISHED,
- ... component='partner')
- >>> pub_binaries = test_publisher.getPubBinaries(
- ... archive=partner_archive, pub_source=pub_source,
- ... distroseries=hoary, status=PackagePublishingStatus.PUBLISHED)
- >>> partner_build = pub_binaries[0].binarypackagerelease.build
- >>> partner_candidate = partner_build.buildqueue_record
- >>> commit()
- >>> LaunchpadZopelessLayer.switchDbUser(config.builddmaster.dbuser)
-
-Now when we dispatch the partner build, since it has one published
-binary in hoary/i386, the partner archive gets included in the builder
-sources_list.
-
- >>> removeSecurityProxy(
- ... a_builder)._dispatchBuildCandidate(partner_candidate)
- ensurepresent called, url=...
- ensurepresent called, url=http://localhost:58000/.../foo_666.dsc
- OkSlave BUILDING
- Archives:
- deb http://ftpmaster.internal/ubuntu hoary
- main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-security
- main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-updates
- main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu-partner hoary partner
- Suite: hoary
- Ogre-component: partner
- Archive Purpose: PARTNER
- Archive Private: False
-
- >>> partner_candidate.destroySelf()
-
-Similarly, PPA builds pass the 'archives' arguments:
-
- >>> from canonical.launchpad.interfaces import IPersonSet
- >>> cprov_archive = getUtility(IPersonSet).getByName('cprov').archive
- >>> removeSecurityProxy(a_build).archive = cprov_archive
- >>> a_builder.virtualized = True
- >>> a_builder.vm_host = 'localhost.ppa'
- >>> commit()
- >>> a_builder.setSlaveForTesting(OkSlave())
- >>> a_builder.is_available
- True
-
- >>> candidate = a_build.queueBuild()
- >>> setupBuildQueue(candidate, a_builder)
- >>> last_stub_mail_count = len(stub.test_emails)
-
-Exactly as Partner, Celso's PPA won't be included if it doesn't
-contain any published binary in hoary/i386. We will create it before
-dispatching.
-
- >>> create_binary_publication_for(
- ... cprov_archive, hoary, PackagePublishingStatus.PUBLISHED)
-
- >>> removeSecurityProxy(a_builder)._dispatchBuildCandidate(candidate)
- ensurepresent called, url=...
- ensurepresent called,
- url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
- OkSlave BUILDING
- Archives:
- deb http://ftpmaster.internal/ubuntu hoary main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-security main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-updates main restricted universe multiverse
- deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
- Suite: hoary
- Ogre-component: main
- Archive Purpose: PPA
- Archive Private: False
-
-If the build is for a private PPA, the slave scanner will pass a
-sources.list entry that contains a password to access the archive.
-
- >>> from canonical.testing import LaunchpadZopelessLayer
- >>> commit()
- >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
- >>> login('foo.bar@xxxxxxxxxxxxx')
- >>> build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(
- ... candidate)
- >>> for build_file in build.source_package_release.files:
- ... removeSecurityProxy(build_file).libraryfile.restricted = True
- >>> private_ppa = factory.makeArchive(
- ... owner=cprov_archive.owner, name='pppa', private=True,
- ... virtualized=False, distribution=ubuntu)
-
-It's necessary to publish some binaries into the private PPA, otherwise
-the PPA won't be included as a dependency in the sources list below.
-
- >>> binaries = test_publisher.getPubBinaries(
- ... distroseries=ubuntu['hoary'], archive=private_ppa,
- ... status=PackagePublishingStatus.PUBLISHED)
- >>> removeSecurityProxy(build).archive = private_ppa
- >>> commit()
- >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
- >>> login(ANONYMOUS)
-
-Dispatch the build again. Celso's archive sources.list entry now has the
-buildd:secret@ part in the URL.
-
-Also note that when ensurepresent() is called, it receives a URL that
-points to the private archive rather than the librarian for the private
-firefox file. This is because the build slaves are not allowed to
-access the restricted librarian as it cannot provide access via
-credentials, unlike the archive itself.
-
-Finally, the archive purpose is overridden to PRIMARY instead of PPA
-for any archives that have require_virtualized as False.
-
-In this circumstance, it also uses the component override from the PRIMARY
-archive and not the one from the PPA, which on the absence of ancestry
-defaults to 'universe'.
-
- >>> build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(candidate)
- >>> print build.current_component.name
- main
-
-This is so that the mangling tools will run over the built packages.
-
- >>> removeSecurityProxy(a_builder)._dispatchBuildCandidate(candidate)
- ensurepresent called, url=...
- ensurepresent called,
- url=http://private-ppa.../cprov/pppa/.../firefox_0.9.2.orig.tar.gz
- URL authorisation with buildd/sekrit
- OkSlave BUILDING
- Archives:
- deb http://buildd:sekrit@private-ppa.../cprov/pppa/ubuntu hoary main
- deb http://ftpmaster.internal/ubuntu hoary
- main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-security
- main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-updates
- main restricted universe multiverse
- Suite: hoary
- Ogre-component: universe
- Archive Purpose: PRIMARY
- Archive Private: True
-
-We will create an ancestry in the primary archive target to the 'main'
-component and this time the dispatching will follow that component.
-
- >>> sourcename = build.source_package_release.name
-
- >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
- >>> login('foo.bar@xxxxxxxxxxxxx')
-
- >>> ancestry = test_publisher.getPubSource(
- ... sourcename=sourcename, version='0.1', distroseries=hoary)
-
- >>> print ancestry.displayname
- mozilla-firefox 0.1 in hoary
-
- >>> print ancestry.component.name
- main
-
- >>> commit()
- >>> LaunchpadZopelessLayer.switchDbUser(config.builddmaster.dbuser)
- >>> login(ANONYMOUS)
-
- >>> removeSecurityProxy(a_builder)._dispatchBuildCandidate(candidate)
- ensurepresent called, ...
- ...
- Ogre-component: main
- ...
-
- >>> candidate.destroySelf()
-
Since this is a build in a private archive, the log was uploaded to
the restricted librarian.
- >>> candidate = a_build.queueBuild()
+ >>> removeSecurityProxy(build).archive = private_ppa
+ >>> commit()
+ >>> candidate = build.queueBuild()
>>> setupBuildQueue(candidate, a_builder)
>>> build.upload_log = None
>>> candidate.builder.setSlaveForTesting(WaitingSlave('BuildStatus.OK'))
@@ -1059,7 +774,7 @@
>>> lfa.restricted
True
>>> print lfa.filename
- buildlog_ubuntu-hoary-i386.mozilla-firefox_0.9_BUILDING.txt.gz
+ buildlog_ubuntu-warty-i386.mozilla-firefox_0.9_BUILDING.txt.gz
The attempt to fetch the buildlog from the common librarian will fail
since this is a build in a private archive and the buildlog was thus
@@ -1079,7 +794,7 @@
... getUtility(IRestrictedLibrarianClient).getFileByAlias(lfa.id))
>>> url_parts = urlparse.urlsplit(lfa2.file.geturl())
>>> print os.path.basename(url_parts[2])
- buildlog_ubuntu-hoary-i386.mozilla-firefox_0.9_BUILDING.txt.gz
+ buildlog_ubuntu-warty-i386.mozilla-firefox_0.9_BUILDING.txt.gz
A PPA can depend on another PPA. We can make Celso's PPA depend on
Mark's PPA:
@@ -1088,218 +803,13 @@
>>> LaunchpadZopelessLayer.switchDbUser('launchpad')
>>> login('foo.bar@xxxxxxxxxxxxx')
-We'll switch the build's archive back to Celso's PPA and set the PPA to
-virtualized before adding the dependency on Mark's PPA.
-
- >>> removeSecurityProxy(build).archive = cprov_archive
- >>> cprov_archive.require_virtualized = True
- >>> for build_file in a_build.source_package_release.files:
- ... removeSecurityProxy(build_file).libraryfile.restricted = False
- >>> mark_archive = getUtility(IPersonSet).getByName('mark').archive
-
- >>> unused_dep = cprov_archive.addArchiveDependency(
- ... mark_archive, PackagePublishingPocket.RELEASE,
- ... getUtility(IComponentSet)['main'])
-
- >>> commit()
- >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
- >>> login(ANONYMOUS)
-
-Now we can see that a build from Celso's PPA will be able to install
-dependencies from Mark's PPA, if Mark's PPA has at least one binary
-published in hoary/i386, which is not the case.
-
- >>> a_builder.setSlaveForTesting(OkSlave())
- >>> a_builder.is_available
- True
-
- >>> candidate = a_build.queueBuild()
- >>> setupBuildQueue(candidate, a_builder)
- >>> last_stub_mail_count = len(stub.test_emails)
-
- >>> removeSecurityProxy(a_builder)._dispatchBuildCandidate(candidate)
- ensurepresent called, url=...
- ensurepresent called,
- url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
- OkSlave BUILDING
- Archives:
- deb http://ftpmaster.internal/ubuntu hoary main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-security main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-updates main restricted universe multiverse
- deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
- Suite: hoary
- Ogre-component: main
- Archive Purpose: PPA
- Archive Private: False
-
-We will create the required publication in Mark's PPA and try again.
-
- >>> candidate.destroySelf()
- >>> a_builder.setSlaveForTesting(OkSlave())
- >>> a_builder.is_available
- True
-
- >>> candidate = a_build.queueBuild()
- >>> setupBuildQueue(candidate, a_builder)
- >>> last_stub_mail_count = len(stub.test_emails)
-
- >>> create_binary_publication_for(
- ... mark_archive, hoary, PackagePublishingStatus.PUBLISHED)
-
- >>> removeSecurityProxy(a_builder)._dispatchBuildCandidate(candidate)
- ensurepresent called, url=...
- ensurepresent called,
- url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
- OkSlave BUILDING
- Archives:
- deb http://ftpmaster.internal/ubuntu hoary main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-security main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-updates main restricted universe multiverse
- deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
- deb http://ppa.launchpad.dev/mark/ppa/ubuntu hoary main
- Suite: hoary
- Ogre-component: main
- Archive Purpose: PPA
- Archive Private: False
Clean up before continuing:
- >>> candidate.destroySelf()
>>> a_builder.virtualized = False
>>> removeSecurityProxy(a_build).archive = ubuntu.main_archive
>>> commit()
-Builddmaster stops before starting to build a denied build.
-Since hoary is in development, we are not able to dispatch
-builds for post-release pockets:
-
- >>> candidate = a_build.queueBuild()
- >>> setupBuildQueue(candidate, a_builder)
- >>> last_stub_mail_count = len(stub.test_emails)
-
-Make a build in the updates pocket:
-
- >>> hoary = hoary_i386.distroseries
- >>> hoary_evo = hoary.getSourcePackage(
- ... 'evolution').currentrelease.sourcepackagerelease
- >>> updates_build = hoary_evo.createBuild(
- ... distro_arch_series=hoary_i386,
- ... pocket=PackagePublishingPocket.UPDATES,
- ... processor=hoary_i386.default_processor,
- ... archive=hoary_i386.main_archive)
- >>> updates_bqItem = updates_build.queueBuild()
-
- >>> hoary_i386.distroseries.status.name
- 'DEVELOPMENT'
- >>> removeSecurityProxy(a_builder)._dispatchBuildCandidate(updates_bqItem)
- Traceback (most recent call last):
- ...
- AssertionError: i386 build of evolution 1.0 in ubuntu hoary UPDATES (...) can not be built for pocket UPDATES: invalid pocket due to the series status of hoary.
-
-== Pocket dependencies ==
-
-Change the distroseries status for testing. FROZEN allows building in
-all pockets:
-
- >>> from canonical.launchpad.interfaces import SeriesStatus
- >>> hoary_i386.distroseries.status = SeriesStatus.FROZEN
-
-Now we can start a build in other pockets, and see what archives are
-passed to the slave.
-
-A build in the updates pocket:
-
- >>> a_builder.currentjob.destroySelf()
-
- >>> bqItem3 = a_build.queueBuild()
- >>> build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(bqItem3)
- >>> removeSecurityProxy(build).pocket = (
- ... PackagePublishingPocket.UPDATES)
- >>> last_stub_mail_count = len(stub.test_emails)
- >>> removeSecurityProxy(a_builder)._dispatchBuildCandidate(bqItem3)
- ensurepresent called, url=...
- ensurepresent called,
- url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
- OkSlave BUILDING
- Archives:
- deb http://ftpmaster.internal/ubuntu hoary main
- deb http://ftpmaster.internal/ubuntu hoary-security main
- deb http://ftpmaster.internal/ubuntu hoary-updates main
- Suite: hoary-updates
- Ogre-component: main
- Archive Purpose: PRIMARY
- Archive Private: False
-
-A build in the proposed pocket:
-
- >>> a_builder.currentjob.destroySelf()
-
- >>> bqItem3 = a_build.queueBuild()
- >>> removeSecurityProxy(build).pocket = (
- ... PackagePublishingPocket.PROPOSED)
- >>> last_stub_mail_count = len(stub.test_emails)
- >>> removeSecurityProxy(a_builder)._dispatchBuildCandidate(bqItem3)
- ensurepresent called, url=...
- ensurepresent called,
- url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
- OkSlave BUILDING
- Archives:
- deb http://ftpmaster.internal/ubuntu hoary main
- deb http://ftpmaster.internal/ubuntu hoary-proposed main
- deb http://ftpmaster.internal/ubuntu hoary-security main
- deb http://ftpmaster.internal/ubuntu hoary-updates main
- Suite: hoary-proposed
- Ogre-component: main
- Archive Purpose: PRIMARY
- Archive Private: False
-
-A build in the backports pocket:
-
- >>> a_builder.currentjob.destroySelf()
-
- >>> bqItem3 = a_build.queueBuild()
- >>> removeSecurityProxy(build).pocket = (
- ... PackagePublishingPocket.BACKPORTS)
- >>> last_stub_mail_count = len(stub.test_emails)
- >>> removeSecurityProxy(a_builder)._dispatchBuildCandidate(bqItem3)
- ensurepresent called, url=...
- ensurepresent called,
- url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
- OkSlave BUILDING
- Archives:
- deb http://ftpmaster.internal/ubuntu hoary main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-backports main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-security main restricted universe multiverse
- deb http://ftpmaster.internal/ubuntu hoary-updates main restricted universe multiverse
- Suite: hoary-backports
- Ogre-component: main
- Archive Purpose: PRIMARY
- Archive Private: False
-
-A build in the security pocket:
-
- >>> a_builder.currentjob.destroySelf()
-
- >>> bqItem3 = a_build.queueBuild()
- >>> removeSecurityProxy(build).status = (
- ... BuildStatus.NEEDSBUILD)
- >>> removeSecurityProxy(build).pocket = (
- ... PackagePublishingPocket.SECURITY)
- >>> last_stub_mail_count = len(stub.test_emails)
-
-The pocket-dependency infrastructure is ready to deal with SECURITY
-pocket, however we explicitly skip security builds when dispatching
-because Embargoed-Archives and Restricted-UI implementations are not
-yet ready.
-
- >>> removeSecurityProxy(a_builder)._dispatchBuildCandidate(bqItem3)
- Traceback (most recent call last):
- ...
- AssertionError: Soyuz is not yet capable of building SECURITY uploads.
-
-Builds for security pocket are marked as FAILEDTOBUILD inside the
-_findBuildCandidate() method, see doc/buildd-dispatching.txt
-
== Builder Status Handler ==
=== modified file 'lib/lp/soyuz/model/binarypackagebuildbehavior.py'
--- lib/lp/soyuz/model/binarypackagebuildbehavior.py 2010-08-23 16:51:11 +0000
+++ lib/lp/soyuz/model/binarypackagebuildbehavior.py 2010-09-21 18:41:18 +0000
@@ -99,8 +99,9 @@
distroseries state.
"""
build = self.build
- assert not (not self._builder.virtualized and build.is_virtualized), (
- "Attempt to build non-virtual item on a virtual builder.")
+ if not self._builder.virtualized and build.is_virtualized:
+ raise AssertionError(
+ "Attempt to build non-virtual item on a virtual builder.")
# Assert that we are not silently building SECURITY jobs.
# See findBuildCandidates. Once we start building SECURITY
=== modified file 'lib/lp/soyuz/scripts/buildd.py'
--- lib/lp/soyuz/scripts/buildd.py 2010-08-27 11:19:54 +0000
+++ lib/lp/soyuz/scripts/buildd.py 2010-09-21 18:41:18 +0000
@@ -276,6 +276,8 @@
self.txn.commit()
+# XXX: This is the old slave scanner. Julian says it's not running on
+# production. We should either delete it or update it to use the async apis.
class SlaveScanner(LaunchpadCronScript):
def main(self):
=== modified file 'lib/lp/soyuz/tests/soyuzbuilddhelpers.py'
--- lib/lp/soyuz/tests/soyuzbuilddhelpers.py 2010-09-20 11:52:51 +0000
+++ lib/lp/soyuz/tests/soyuzbuilddhelpers.py 2010-09-21 18:41:18 +0000
@@ -17,10 +17,8 @@
]
from StringIO import StringIO
-import subprocess
import xmlrpclib
-from canonical.config import config
from lp.buildmaster.interfaces.builder import CannotFetchFile
from lp.buildmaster.model.builder import (
rescueBuilderIfLost,
@@ -31,6 +29,8 @@
)
from lp.testing.sampledata import I386_ARCHITECTURE_NAME
+# XXX: Almost everything in this module will need to be revisited & perhaps
+# deleted now that we have new APIs for BuilderSlave.
class MockBuilder:
"""Emulates a IBuilder class."""
@@ -85,68 +85,53 @@
updateBuilderStatus(self, logger)
+# XXX: It would be *really* nice to run some set of tests against the real
+# BuilderSlave and this one to prevent interface skew.
class OkSlave:
"""An idle mock slave that prints information about itself.
The architecture tag can be customised during initialisation."""
def __init__(self, arch_tag=I386_ARCHITECTURE_NAME):
+ self.call_log = []
self.arch_tag = arch_tag
def status(self):
return ('BuilderStatus.IDLE', '')
def ensurepresent(self, sha1, url, user=None, password=None):
- print "ensurepresent called, url=%s" % url
- if user is not None and user != "":
- print "URL authorisation with %s/%s" % (user, password)
+ self.call_log.append(('ensurepresent', url, user, password))
return True, None
def build(self, buildid, buildtype, chroot, filemap, args):
+ self.call_log.append(
+ ('build', buildid, buildtype, chroot, filemap.keys(), args))
info = 'OkSlave BUILDING'
- print info
- if 'archives' in args:
- print "Archives:"
- for archive_line in sorted(args['archives']):
- print " %s" % archive_line
- else:
- print "No archives set."
- print "Suite: %s" % args['suite']
- print "Ogre-component: %s" % args['ogrecomponent']
- print "Archive Purpose: %s" % args['archive_purpose']
- print "Archive Private: %s" % args['archive_private']
return ('BuildStatus.Building', info)
- def fetchlogtail(self, size):
- return 'BOGUS'
-
def echo(self, *args):
+ self.call_log.append(('echo',) + args)
return args
def clean(self):
- pass
+ self.call_log.append('clean')
def abort(self):
- pass
+ self.call_log.append('abort')
def info(self):
+ self.call_log.append('info')
return ('1.0', self.arch_tag, 'debian')
- def resume(self):
- resume_argv = config.builddmaster.vm_resume_command.split()
- resume_process = subprocess.Popen(
- resume_argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- stdout, stderr = resume_process.communicate()
-
- return (stdout, stderr, resume_process.returncode)
-
def sendFileToSlave(self, sha1, url, username="", password=""):
+ self.call_log.append('sendFileToSlave')
present, info = self.ensurepresent(sha1, url, username, password)
if not present:
raise CannotFetchFile(url, info)
def cacheFile(self, logger, libraryfilealias):
- self.sendFileToSlave(
+ self.call_log.append('cacheFile')
+ return self.sendFileToSlave(
libraryfilealias.content.sha1, libraryfilealias.http_url)
@@ -158,10 +143,12 @@
self.build_id = build_id
def status(self):
+ self.call_log.append('status')
buildlog = xmlrpclib.Binary("This is a build log")
return ('BuilderStatus.BUILDING', self.build_id, buildlog)
def getFile(self, sum):
+ self.call_log.append('getFile')
if sum == "buildlog":
s = StringIO("This is a build log")
s.headers = {'content-length': 19}
@@ -183,10 +170,12 @@
self.valid_file_hashes = ['buildlog']
def status(self):
+ self.call_log.append('status')
return ('BuilderStatus.WAITING', self.state, self.build_id, {},
self.dependencies)
def getFile(self, hash):
+ self.call_log.append('getFile')
if hash in self.valid_file_hashes:
content = "This is a %s" % hash
s = StringIO(content)
@@ -198,6 +187,7 @@
"""A mock slave that looks like it's in the process of aborting."""
def status(self):
+ self.call_log.append('status')
return ('BuilderStatus.ABORTING', '1-1')
@@ -205,6 +195,7 @@
"""A mock slave that looks like it's aborted."""
def status(self):
+ self.call_log.append('status')
return ('BuilderStatus.ABORTED', '1-1')
@@ -214,10 +205,15 @@
When 'aborted' it raises an xmlrpclib.Fault(8002, 'Could not abort')
"""
+ def __init__(self):
+ self.call_log = []
+
def status(self):
+ self.call_log.append('status')
return ('BuilderStatus.BUILDING', '1000-10000')
def abort(self):
+ self.call_log.append('abort')
raise xmlrpclib.Fault(8002, "Could not abort")
@@ -225,4 +221,5 @@
"""A mock slave that reports that it is broken."""
def status(self):
+ self.call_log.append('status')
raise xmlrpclib.Fault(8001, "Broken slave")
=== added file 'lib/lp/soyuz/tests/test_binarypackagebuildbehavior.py'
--- lib/lp/soyuz/tests/test_binarypackagebuildbehavior.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/tests/test_binarypackagebuildbehavior.py 2010-09-21 18:41:18 +0000
@@ -0,0 +1,205 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+from __future__ import with_statement
+
+"""Tests for BinaryPackageBuildBehavior."""
+
+__metaclass__ = type
+
+from twisted.internet import defer
+from twisted.trial import unittest
+
+from zope.security.proxy import removeSecurityProxy
+
+from canonical.launchpad.scripts.logger import QuietFakeLogger
+from canonical.testing import TwistedLaunchpadZopelessLayer
+
+from lp.registry.interfaces.pocket import (
+ PackagePublishingPocket,
+ pocketsuffix,
+ )
+from lp.registry.interfaces.series import SeriesStatus
+from lp.soyuz.adapters.archivedependencies import (
+ get_sources_list_for_building,
+ )
+from lp.soyuz.enums import (
+ ArchivePurpose,
+ )
+from lp.soyuz.tests.soyuzbuilddhelpers import OkSlave
+from lp.testing import (
+ ANONYMOUS,
+ login_as,
+ logout,
+ )
+from lp.testing.factory import LaunchpadObjectFactory
+
+
+class TestBinaryBuildPackageBehavior(unittest.TestCase):
+ """Tests for the BinaryPackageBuildBehavior.
+
+ In particular, these tests are about how the BinaryPackageBuildBehavior
+ interacts with the build slave. We test this by using a test double that
+ implements the same interface as `BuilderSlave` but instead of actually
+ making XML-RPC calls, just records any method invocations along with
+ interesting parameters.
+ """
+
+ layer = TwistedLaunchpadZopelessLayer
+
+ def setUp(self):
+ super(TestBinaryBuildPackageBehavior, self).setUp()
+ self.factory = LaunchpadObjectFactory()
+ login_as(ANONYMOUS)
+ self.addCleanup(logout)
+ self.layer.switchDbUser('testadmin')
+
+ def assertSlaveInteraction(self, ignored, call_log, builder, build,
+ chroot, archive, archive_purpose, component,
+ extra_urls=None, filemap_names=None):
+ """Assert that 'call_log' matches our expectations of interaction.
+
+ 'call_log' is expected to be a recording from a test double slave like
+ OkSlave or one its subclasses. We assert that the calls in call_log
+ match our expectations, thus showing that the binary package behaviour
+ interacts with the slave in the way we expect.
+
+ :param ignored: Ignored. This parameter here only to make it easier
+ to use the assertion as a Twisted callback.
+ :param call_log: A list of calls to a `BuilderSlave`-like object.
+ :param builder: The builder we are using to build the binary package.
+ :param build: The build being done on the builder.
+ :param chroot: The `LibraryFileAlias` for the chroot in which we are
+ building.
+ :param archive: The `IArchive` into which we are building.
+ :param archive_purpose: The ArchivePurpose we are sending to the
+ builder. We specify this separately from the archive because
+ sometimes the behavior object has to give a different purpose
+ in order to trick the slave into building correctly.
+ """
+ job = removeSecurityProxy(builder.current_build_behavior).buildfarmjob
+ build_id = job.generateSlaveBuildCookie()
+ ds_name = build.distro_arch_series.distroseries.name
+ suite = ds_name + pocketsuffix[build.pocket]
+ archives = get_sources_list_for_building(
+ build, build.distro_arch_series,
+ build.source_package_release.name)
+ arch_indep = build.distro_arch_series.isNominatedArchIndep
+ if filemap_names is None:
+ filemap_names = []
+ if extra_urls is None:
+ extra_urls = []
+
+ def make_expected_upload(url):
+ return [
+ 'cacheFile',
+ 'sendFileToSlave',
+ ('ensurepresent', url, '', '')]
+
+ expected = []
+ for url in [chroot.http_url] + extra_urls:
+ expected.extend(make_expected_upload(url))
+ expected.extend([
+ ('build', build_id, 'binarypackage', chroot.content.sha1,
+ filemap_names,
+ {'arch_indep': arch_indep,
+ 'arch_tag': build.distro_arch_series.architecturetag,
+ 'archive_private': archive.private,
+ 'archive_purpose': archive_purpose.name,
+ 'archives': archives,
+ 'build_debug_symbols': archive.build_debug_symbols,
+ 'ogrecomponent': component,
+ 'suite': suite})])
+ self.assertEqual(call_log, expected)
+
+ def startBuild(self, builder, candidate):
+ builder = removeSecurityProxy(builder)
+ candidate = removeSecurityProxy(candidate)
+ return defer.maybeDeferred(
+ builder.startBuild, candidate, QuietFakeLogger())
+
+ def test_non_virtual_ppa_dispatch(self):
+ # When the BinaryPackageBuildBehavior dispatches PPA builds to
+ # non-virtual builders, it stores the chroot on the server and
+ # requests a binary package build, lying to say that the archive
+ # purpose is "PRIMARY" because this ensures that the package mangling
+ # tools will run over the built packages.
+ archive = self.factory.makeArchive(virtualized=False)
+ slave = OkSlave()
+ builder = self.factory.makeBuilder(virtualized=False)
+ builder.setSlaveForTesting(slave)
+ build = self.factory.makeBinaryPackageBuild(
+ builder=builder, archive=archive)
+ lf = self.factory.makeLibraryFileAlias()
+ self.layer.txn.commit()
+ build.distro_arch_series.addOrUpdateChroot(lf)
+ candidate = build.queueBuild()
+ d = self.startBuild(builder, candidate)
+ d.addCallback(
+ self.assertSlaveInteraction,
+ slave.call_log, builder, build, lf, archive,
+ ArchivePurpose.PRIMARY, 'universe')
+ return d
+
+ def test_partner_dispatch_no_publishing_history(self):
+ archive = self.factory.makeArchive(
+ virtualized=False, purpose=ArchivePurpose.PARTNER)
+ slave = OkSlave()
+ builder = self.factory.makeBuilder(virtualized=False)
+ builder.setSlaveForTesting(slave)
+ build = self.factory.makeBinaryPackageBuild(
+ builder=builder, archive=archive)
+ lf = self.factory.makeLibraryFileAlias()
+ self.layer.txn.commit()
+ build.distro_arch_series.addOrUpdateChroot(lf)
+ candidate = build.queueBuild()
+ d = self.startBuild(builder, candidate)
+ d.addCallback(
+ self.assertSlaveInteraction,
+ slave.call_log, builder, build, lf, archive,
+ ArchivePurpose.PARTNER, build.current_component.name)
+ return d
+
+ def test_dont_dispatch_release_builds(self):
+ archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
+ builder = self.factory.makeBuilder()
+ distroseries = self.factory.makeDistroSeries(
+ status=SeriesStatus.CURRENT, distribution=archive.distribution)
+ distro_arch_series = self.factory.makeDistroArchSeries(
+ distroseries=distroseries)
+ build = self.factory.makeBinaryPackageBuild(
+ builder=builder, archive=archive,
+ distroarchseries=distro_arch_series,
+ pocket=PackagePublishingPocket.RELEASE)
+ lf = self.factory.makeLibraryFileAlias()
+ self.layer.txn.commit()
+ build.distro_arch_series.addOrUpdateChroot(lf)
+ candidate = build.queueBuild()
+ behavior = candidate.required_build_behavior
+ behavior.setBuilder(build)
+ e = self.assertRaises(
+ AssertionError, behavior.verifyBuildRequest, QuietFakeLogger())
+ self.assertEqual(
+ "%s (%s) can not be built for pocket %s: invalid pocket due "
+ "to the series status of %s." % (
+ build.title, build.id, build.pocket.name,
+ build.distro_series.name),
+ str(e))
+
+ def test_dont_dispatch_security_builds(self):
+ archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
+ builder = self.factory.makeBuilder()
+ build = self.factory.makeBinaryPackageBuild(
+ builder=builder, archive=archive,
+ pocket=PackagePublishingPocket.SECURITY)
+ lf = self.factory.makeLibraryFileAlias()
+ self.layer.txn.commit()
+ build.distro_arch_series.addOrUpdateChroot(lf)
+ candidate = build.queueBuild()
+ behavior = candidate.required_build_behavior
+ behavior.setBuilder(build)
+ e = self.assertRaises(
+ AssertionError, behavior.verifyBuildRequest, QuietFakeLogger())
+ self.assertEqual(
+ 'Soyuz is not yet capable of building SECURITY uploads.',
+ str(e))
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2010-09-21 11:45:15 +0000
+++ lib/lp/testing/factory.py 2010-09-21 18:41:18 +0000
@@ -1944,6 +1944,9 @@
processorfamily = ProcessorFamilySet().getByName('powerpc')
if owner is None:
owner = self.makePerson()
+ # XXX: architecturetag & processerfamily are tightly coupled. It's
+ # wrong to just make a fresh architecture tag without also making a
+ # processor family to go with it (ideally with processors!)
if architecturetag is None:
architecturetag = self.getUniqueString('arch')
return distroseries.newArch(
@@ -2625,7 +2628,7 @@
def makeBinaryPackageBuild(self, source_package_release=None,
distroarchseries=None, archive=None, builder=None,
- status=None):
+ status=None, pocket=None):
"""Create a BinaryPackageBuild.
If archive is not supplied, the source_package_release is used
@@ -2656,13 +2659,15 @@
processorfamily=processor.family)
if status is None:
status = BuildStatus.NEEDSBUILD
+ if pocket is None:
+ pocket = PackagePublishingPocket.RELEASE
binary_package_build = getUtility(IBinaryPackageBuildSet).new(
source_package_release=source_package_release,
processor=processor,
distro_arch_series=distroarchseries,
status=status,
archive=archive,
- pocket=PackagePublishingPocket.RELEASE,
+ pocket=pocket,
date_created=self.getUniqueDate())
naked_build = removeSecurityProxy(binary_package_build)
naked_build.builder = builder
=== modified file 'lib/lp/testing/tests/test_factory.py'
--- lib/lp/testing/tests/test_factory.py 2010-09-21 11:08:26 +0000
+++ lib/lp/testing/tests/test_factory.py 2010-09-21 18:41:18 +0000
@@ -113,12 +113,26 @@
status=BuildStatus.FULLYBUILT)
self.assertEqual(BuildStatus.FULLYBUILT, bpb.status)
- def test_makeBinaryPackageBuild_can_be_queued(self):
- build = self.factory.makeBinaryPackageBuild()
- # Just check that makeBinaryPackageBuild returns a build that can be
- # queued.
- build.queueBuild()
-
+<<<<<<< TREE
+ def test_makeBinaryPackageBuild_can_be_queued(self):
+ build = self.factory.makeBinaryPackageBuild()
+ # Just check that makeBinaryPackageBuild returns a build that can be
+ # queued.
+ build.queueBuild()
+
+=======
+ def test_makeBinaryPackageBuild_uses_pocket(self):
+ bpb = self.factory.makeBinaryPackageBuild(
+ pocket=PackagePublishingPocket.UPDATES)
+ self.assertEqual(PackagePublishingPocket.UPDATES, bpb.pocket)
+
+ def test_makeBinaryPackageBuild_can_be_queued(self):
+ build = self.factory.makeBinaryPackageBuild()
+ # Just check that makeBinaryPackageBuild returns a build that can be
+ # queued.
+ build.queueBuild()
+
+>>>>>>> MERGE-SOURCE
# makeBinaryPackageName
def test_makeBinaryPackageName_returns_proxied_IBinaryPackageName(self):
binarypackagename = self.factory.makeBinaryPackageName()