launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #04691
[Merge] lp:~abentley/launchpad/upgrade-not-branch-error into lp:launchpad
Aaron Bentley has proposed merging lp:~abentley/launchpad/upgrade-not-branch-error into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #823485 in Launchpad itself: "NotBranchError raised by upgrade_branches.py script"
https://bugs.launchpad.net/launchpad/+bug/823485
For more details, see:
https://code.launchpad.net/~abentley/launchpad/upgrade-not-branch-error/+merge/72242
= Summary =
Fix bug #823485: NotBranchError raised by upgrade_branches.py script
== Proposed fix ==
Handle NotBranchError as a user error and email to user.
== Pre-implementation notes ==
None
== Implementation details ==
The error notification should go to the user who requested the upgrade, but
until now, the requester was not recorded. Now, BranchUpgradeJob.create
requires a requester, and various tests are updated to match.
== Tests ==
bin/test -t test_branch -t test_branchjob -t test_upgrade_branches -t test_garbo
== Demo and Q/A ==
Create a branch. Request an upgrade. Immediately delete the contents of the
branch (but don't delete the branch from the database.)
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/code/browser/branch.py
lib/lp/code/interfaces/branch.py
lib/lp/code/model/tests/test_branchjob.py
database/schema/security.cfg
lib/lp/testing/__init__.py
lib/lp/scripts/tests/test_garbo.py
lib/lp/code/model/branch.py
lib/lp/code/scripts/tests/test_upgrade_branches.py
lib/lp/code/model/tests/test_branch.py
lib/lp/code/model/branchjob.py
--
https://code.launchpad.net/~abentley/launchpad/upgrade-not-branch-error/+merge/72242
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~abentley/launchpad/upgrade-not-branch-error into lp:launchpad.
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2011-08-17 19:51:38 +0000
+++ database/schema/security.cfg 2011-08-19 18:26:41 +0000
@@ -1903,7 +1903,9 @@
groups=script
public.branch = SELECT, UPDATE
public.branchjob = SELECT, INSERT
+public.emailaddress = SELECT
public.job = SELECT, INSERT, UPDATE
+public.person = SELECT
type=user
[send-branch-mail]
=== modified file 'lib/lp/code/browser/branch.py'
--- lib/lp/code/browser/branch.py 2011-07-21 02:18:54 +0000
+++ lib/lp/code/browser/branch.py 2011-08-19 18:26:41 +0000
@@ -47,7 +47,6 @@
)
from lazr.uri import URI
import pytz
-import simplejson
from zope.app.form.browser import TextAreaWidget
from zope.component import (
getUtility,
@@ -838,7 +837,7 @@
def mirror_of_ssh(self):
"""True if this a mirror branch with an sftp or bzr+ssh URL."""
if not self.context.url:
- return False # not a mirror branch
+ return False # not a mirror branch
uri = URI(self.context.url)
return uri.scheme in ('sftp', 'bzr+ssh')
@@ -994,7 +993,7 @@
@action('Upgrade', name='upgrade_branch')
def upgrade_branch_action(self, action, data):
- self.context.requestUpgrade()
+ self.context.requestUpgrade(self.user)
class BranchEditView(BranchEditFormView, BranchNameValidationMixin):
=== modified file 'lib/lp/code/interfaces/branch.py'
--- lib/lp/code/interfaces/branch.py 2011-08-17 10:01:10 +0000
+++ lib/lp/code/interfaces/branch.py 2011-08-19 18:26:41 +0000
@@ -1063,7 +1063,7 @@
adapted to an IBranchTarget.
"""
- def requestUpgrade():
+ def requestUpgrade(requester):
"""Create an IBranchUpgradeJob to upgrade this branch."""
def branchChanged(stacked_on_url, last_revision_id, control_format,
=== modified file 'lib/lp/code/model/branch.py'
--- lib/lp/code/model/branch.py 2011-08-17 20:21:01 +0000
+++ lib/lp/code/model/branch.py 2011-08-19 18:26:41 +0000
@@ -1152,10 +1152,10 @@
BranchJob.job_type == BranchJobType.UPGRADE_BRANCH)
return jobs.count() > 0
- def requestUpgrade(self):
+ def requestUpgrade(self, requester):
"""See `IBranch`."""
from lp.code.interfaces.branchjob import IBranchUpgradeJobSource
- return getUtility(IBranchUpgradeJobSource).create(self)
+ return getUtility(IBranchUpgradeJobSource).create(self, requester)
def _checkBranchVisibleByUser(self, user):
"""Is *this* branch visible by the user.
=== modified file 'lib/lp/code/model/branchjob.py'
--- lib/lp/code/model/branchjob.py 2011-07-18 20:46:27 +0000
+++ lib/lp/code/model/branchjob.py 2011-08-19 18:26:41 +0000
@@ -21,7 +21,10 @@
from bzrlib.branch import Branch as BzrBranch
from bzrlib.diff import show_diff_trees
-from bzrlib.errors import NoSuchFile
+from bzrlib.errors import (
+ NoSuchFile,
+ NotBranchError,
+ )
from bzrlib.log import (
log_formatter,
show_log,
@@ -107,6 +110,7 @@
from lp.services.job.interfaces.job import JobStatus
from lp.services.job.model.job import Job
from lp.services.job.runner import BaseRunnableJob
+from lp.services.mail.sendmail import format_address_for_person
from lp.translations.interfaces.translationimportqueue import (
ITranslationImportQueue,
)
@@ -275,6 +279,11 @@
vars.append(('branch_name', self.context.branch.unique_name))
return vars
+ def getErrorRecipients(self):
+ if self.requester is None:
+ return []
+ return [format_address_for_person(self.requester)]
+
class BranchDiffJob(BranchJobDerived):
"""A Job that calculates the a diff related to a Branch."""
@@ -361,14 +370,20 @@
classProvides(IBranchUpgradeJobSource)
class_job_type = BranchJobType.UPGRADE_BRANCH
+ user_error_types = (NotBranchError,)
+
+ def getOperationDescription(self):
+ return 'upgrading a branch'
+
@classmethod
- def create(cls, branch):
+ def create(cls, branch, requester):
"""See `IBranchUpgradeJobSource`."""
if not branch.needs_upgrading:
raise AssertionError('Branch does not need upgrading.')
if branch.upgrade_pending:
raise AssertionError('Branch already has upgrade pending.')
- branch_job = BranchJob(branch, BranchJobType.UPGRADE_BRANCH, {})
+ branch_job = BranchJob(
+ branch, BranchJobType.UPGRADE_BRANCH, {}, requester=requester)
return cls(branch_job)
@staticmethod
=== modified file 'lib/lp/code/model/tests/test_branch.py'
--- lib/lp/code/model/tests/test_branch.py 2011-08-09 14:53:07 +0000
+++ lib/lp/code/model/tests/test_branch.py 2011-08-19 18:26:41 +0000
@@ -617,7 +617,7 @@
owner = removeSecurityProxy(branch).owner
login_person(owner)
self.addCleanup(logout)
- branch.requestUpgrade()
+ branch.requestUpgrade(branch.owner)
self.assertFalse(branch.needs_upgrading)
@@ -682,7 +682,7 @@
owner = removeSecurityProxy(branch).owner
login_person(owner)
self.addCleanup(logout)
- job = removeSecurityProxy(branch.requestUpgrade())
+ job = removeSecurityProxy(branch.requestUpgrade(branch.owner))
jobs = list(getUtility(IBranchUpgradeJobSource).iterReady())
self.assertEqual(
@@ -698,7 +698,7 @@
owner = removeSecurityProxy(branch).owner
login_person(owner)
self.addCleanup(logout)
- self.assertRaises(AssertionError, branch.requestUpgrade)
+ self.assertRaises(AssertionError, branch.requestUpgrade, branch.owner)
def test_requestUpgrade_upgrade_pending(self):
# If there is a pending upgrade already requested, requestUpgrade
@@ -708,9 +708,9 @@
owner = removeSecurityProxy(branch).owner
login_person(owner)
self.addCleanup(logout)
- branch.requestUpgrade()
+ branch.requestUpgrade(branch.owner)
- self.assertRaises(AssertionError, branch.requestUpgrade)
+ self.assertRaises(AssertionError, branch.requestUpgrade, branch.owner)
def test_upgradePending(self):
# If there is a BranchUpgradeJob pending for the branch, return True.
@@ -719,7 +719,7 @@
owner = removeSecurityProxy(branch).owner
login_person(owner)
self.addCleanup(logout)
- branch.requestUpgrade()
+ branch.requestUpgrade(branch.owner)
self.assertTrue(branch.upgrade_pending)
@@ -737,7 +737,7 @@
owner = removeSecurityProxy(branch).owner
login_person(owner)
self.addCleanup(logout)
- branch_job = removeSecurityProxy(branch.requestUpgrade())
+ branch_job = removeSecurityProxy(branch.requestUpgrade(branch.owner))
branch_job.job.start()
branch_job.job.complete()
@@ -1430,7 +1430,6 @@
package.distribution.owner,
package.development_version.setBranch,
pocket, branch, package.distribution.owner)
- series_set = getUtility(IFindOfficialBranchLinks)
self.assertEqual(
{package: ('alter',
_('Branch is officially linked to a source package.'))},
=== modified file 'lib/lp/code/model/tests/test_branchjob.py'
--- lib/lp/code/model/tests/test_branchjob.py 2011-08-12 11:37:08 +0000
+++ lib/lp/code/model/tests/test_branchjob.py 2011-08-19 18:26:41 +0000
@@ -15,7 +15,10 @@
Branch,
BzrBranchFormat7,
)
-from bzrlib.bzrdir import BzrDirMetaFormat1
+from bzrlib.bzrdir import (
+ BzrDir,
+ BzrDirMetaFormat1,
+ )
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack6
from bzrlib.revision import NULL_REVISION
from bzrlib.transport import get_transport
@@ -77,6 +80,7 @@
from lp.scripts.helpers import TransactionFreeOperation
from lp.services.job.interfaces.job import JobStatus
from lp.services.job.model.job import Job
+from lp.services.job.runner import JobRunner
from lp.services.osutils import override_environ
from lp.testing import TestCaseWithFactory
from lp.testing.mail_helpers import pop_notifications
@@ -291,20 +295,18 @@
branch = self.factory.makeAnyBranch(
branch_format=BranchFormat.BZR_BRANCH_5,
repository_format=RepositoryFormat.BZR_REPOSITORY_4)
- job = BranchUpgradeJob.create(branch)
+ job = BranchUpgradeJob.create(branch, self.factory.makePerson())
verifyObject(IBranchUpgradeJob, job)
def test_upgrades_branch(self):
"""Ensure that a branch with an outdated format is upgraded."""
self.useBzrBranches(direct_database=True)
- db_branch, tree = self.create_branch_and_tree(format='knit')
- db_branch.branch_format = BranchFormat.BZR_BRANCH_5
- db_branch.repository_format = RepositoryFormat.BZR_KNIT_1
+ db_branch, tree = self.create_knit()
self.assertEqual(
tree.branch.repository._format.get_format_string(),
'Bazaar-NG Knit Repository Format 1')
- job = BranchUpgradeJob.create(db_branch)
+ job = BranchUpgradeJob.create(db_branch, self.factory.makePerson())
self.becomeDbUser(config.upgrade_branches.dbuser)
with TransactionFreeOperation.require():
job.run()
@@ -321,15 +323,21 @@
branch = self.factory.makeAnyBranch(
branch_format=BranchFormat.BZR_BRANCH_7,
repository_format=RepositoryFormat.BZR_CHK_2A)
- self.assertRaises(AssertionError, BranchUpgradeJob.create, branch)
+ self.assertRaises(
+ AssertionError, BranchUpgradeJob.create, branch,
+ self.factory.makePerson())
+
+ def create_knit(self):
+ db_branch, tree = self.create_branch_and_tree(format='knit')
+ db_branch.branch_format = BranchFormat.BZR_BRANCH_5
+ db_branch.repository_format = RepositoryFormat.BZR_KNIT_1
+ return db_branch, tree
def test_existing_bzr_backup(self):
# If the target branch already has a backup.bzr dir, the upgrade copy
# should remove it.
self.useBzrBranches(direct_database=True)
- db_branch, tree = self.create_branch_and_tree(format='knit')
- db_branch.branch_format = BranchFormat.BZR_BRANCH_5
- db_branch.repository_format = RepositoryFormat.BZR_KNIT_1
+ db_branch, tree = self.create_knit()
# Add a fake backup.bzr dir
source_branch_transport = get_transport(db_branch.getInternalBzrUrl())
@@ -337,7 +345,7 @@
source_branch_transport.clone('.bzr').copy_tree_to_transport(
source_branch_transport.clone('backup.bzr'))
- job = BranchUpgradeJob.create(db_branch)
+ job = BranchUpgradeJob.create(db_branch, self.factory.makePerson())
self.becomeDbUser(config.upgrade_branches.dbuser)
job.run()
@@ -355,6 +363,27 @@
branch.branchChanged('', 'new-id', None, None, None)
Store.of(branch).flush()
+ def test_not_branch_error(self):
+ self.useBzrBranches(direct_database=True)
+ db_branch, tree = self.create_branch_and_tree()
+ branch2 = BzrDir.create_branch_convenience('.')
+ tree.branch.set_stacked_on_url(branch2.base)
+ branch2.bzrdir.destroy_branch()
+ # Create BranchUpgradeJob manually, because we're trying to upgrade a
+ # branch that doesn't need upgrading.
+ requester = self.factory.makePerson()
+ branch_job = BranchJob(
+ db_branch, BranchJobType.UPGRADE_BRANCH, {}, requester=requester)
+ job = BranchUpgradeJob(branch_job)
+ self.becomeDbUser(config.upgrade_branches.dbuser)
+ runner = JobRunner([job])
+ with self.noOops():
+ runner.runJobHandleError(job)
+ (mail,) = pop_notifications()
+ self.assertEqual(
+ 'Launchpad error while upgrading a branch', mail['subject'])
+ self.assertIn('Not a branch', mail.get_payload(decode=True))
+
class TestRevisionMailJob(TestCaseWithFactory):
"""Tests for BranchDiffJob."""
@@ -898,7 +927,7 @@
self.layer.switchDbUser('branchscanner')
self.updateDBRevisions(db_branch, tree.branch)
expected = (
- u"-"*60 + '\n'
+ u"-" * 60 + '\n'
"revno: 1" '\n'
"committer: Joe Bloggs <joe@xxxxxxxxxxx>" '\n'
"branch nick: %s" '\n'
@@ -912,7 +941,7 @@
job.getRevisionMessage(first_revision, 1), expected)
expected_message = (
- u"-"*60 + '\n'
+ u"-" * 60 + '\n'
"revno: 2" '\n'
"committer: Joe Bloggs <joe@xxxxxxxxxxx>" '\n'
"branch nick: %s" '\n'
@@ -988,7 +1017,7 @@
super(TestRosettaUploadJob, self).setUp()
self.series = None
- def _makeBranchWithTreeAndFile(self, file_name, file_content = None):
+ def _makeBranchWithTreeAndFile(self, file_name, file_content=None):
return self._makeBranchWithTreeAndFiles(((file_name, file_content), ))
def _makeBranchWithTreeAndFiles(self, files):
@@ -1035,7 +1064,7 @@
try:
file_content = file_pair[1]
if file_content is None:
- raise IndexError # Same as if missing.
+ raise IndexError # Same as if missing.
except IndexError:
file_content = self.factory.getUniqueString()
dname = os.path.dirname(file_name)
@@ -1060,7 +1089,7 @@
self.series.branch = self.branch
self.series.translations_autoimport_mode = mode
- def _runJobWithFile(self, import_mode, file_name, file_content = None):
+ def _runJobWithFile(self, import_mode, file_name, file_content=None):
return self._runJobWithFiles(
import_mode, ((file_name, file_content), ))
@@ -1293,7 +1322,7 @@
# iterReady does not return jobs for branches where last_scanned_id
# and last_mirror_id are different.
self._makeBranchWithTreeAndFiles([])
- self.branch.last_scanned_id = NULL_REVISION # Was not scanned yet.
+ self.branch.last_scanned_id = NULL_REVISION # Was not scanned yet.
self._makeProductSeries(
TranslationsBranchImportMode.IMPORT_TEMPLATES)
# Put the job in ready state.
=== modified file 'lib/lp/code/scripts/tests/test_upgrade_branches.py'
--- lib/lp/code/scripts/tests/test_upgrade_branches.py 2010-10-04 19:50:45 +0000
+++ lib/lp/code/scripts/tests/test_upgrade_branches.py 2011-08-19 18:26:41 +0000
@@ -34,7 +34,7 @@
target_tree.branch.repository._format.get_format_string(),
'Bazaar-NG Knit Repository Format 1')
- BranchUpgradeJob.create(target)
+ BranchUpgradeJob.create(target, self.factory.makePerson())
transaction.commit()
retcode, stdout, stderr = run_script(
@@ -62,7 +62,7 @@
target_tree.branch.repository._format.get_format_string(),
'Bazaar-NG Knit Repository Format 1')
- BranchUpgradeJob.create(target)
+ BranchUpgradeJob.create(target, self.factory.makePerson())
transaction.commit()
retcode, stdout, stderr = run_script(
=== modified file 'lib/lp/scripts/tests/test_garbo.py'
--- lib/lp/scripts/tests/test_garbo.py 2011-08-17 10:43:26 +0000
+++ lib/lp/scripts/tests/test_garbo.py 2011-08-19 18:26:41 +0000
@@ -848,7 +848,8 @@
db_branch.branch_format = BranchFormat.BZR_BRANCH_5
db_branch.repository_format = RepositoryFormat.BZR_KNIT_1
Store.of(db_branch).flush()
- branch_job = BranchUpgradeJob.create(db_branch)
+ branch_job = BranchUpgradeJob.create(
+ db_branch, self.factory.makePerson())
branch_job.job.date_finished = THIRTY_DAYS_AGO
self.assertEqual(
@@ -876,13 +877,14 @@
branch_format=BranchFormat.BZR_BRANCH_5,
repository_format=RepositoryFormat.BZR_KNIT_1)
- branch_job = BranchUpgradeJob.create(db_branch)
+ branch_job = BranchUpgradeJob.create(
+ db_branch, self.factory.makePerson())
branch_job.job.date_finished = THIRTY_DAYS_AGO
db_branch2 = self.factory.makeAnyBranch(
branch_format=BranchFormat.BZR_BRANCH_5,
repository_format=RepositoryFormat.BZR_KNIT_1)
- BranchUpgradeJob.create(db_branch2)
+ BranchUpgradeJob.create(db_branch2, self.factory.makePerson())
self.runDaily()
=== modified file 'lib/lp/testing/__init__.py'
--- lib/lp/testing/__init__.py 2011-08-15 09:21:48 +0000
+++ lib/lp/testing/__init__.py 2011-08-19 18:26:41 +0000
@@ -437,13 +437,22 @@
'Events were generated: %s.' % event_list)
return result
+ @contextmanager
+ def noOops(self):
+ oops = errorlog.globalErrorUtility.getLastOopsReport()
+ try:
+ yield
+ finally:
+ self.assertNoNewOops(oops)
+
def assertNoNewOops(self, old_oops):
"""Assert that no oops has been recorded since old_oops."""
oops = errorlog.globalErrorUtility.getLastOopsReport()
if old_oops is None:
self.assertIs(None, oops)
else:
- self.assertEqual(oops.id, old_oops.id)
+ self.assertTrue(
+ oops.id == old_oops.id, 'Oops recorded: %s' % oops.id)
def assertSqlAttributeEqualsDate(self, sql_object, attribute_name, date):
"""Fail unless the value of the attribute is equal to the date.