Merge lp:~jtv/launchpad/bug-733132 into lp:launchpad

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Jeroen T. Vermeulen
Approved revision: no longer in the source branch.
Merged at revision: 12588
Proposed branch: lp:~jtv/launchpad/bug-733132
Merge into: lp:launchpad
Prerequisite: lp:~jtv/launchpad/bug-730460-job-class
Diff against target: 127 lines (+60/-1)
3 files modified
lib/lp/services/features/flags.py (+4/-0)
lib/lp/soyuz/model/distroseriesdifferencejob.py (+6/-0)
lib/lp/soyuz/tests/test_distroseriesdifferencejob.py (+50/-1)
To merge this branch: bzr merge lp:~jtv/launchpad/bug-733132
Reviewer Review Type Date Requested Status
Abel Deuring (community) code Approve
Review via email: mp+53009@code.launchpad.net

This proposal supersedes a proposal from 2011-03-11.

Commit message

[r=adeuring][bug=733132] DistroSeriesDifferenceJob feature flag.

Description of the change

= Summary =

Add feature flag to control generation of DistroSeriesDifferenceJobs.

Branch builds on the as-yet unlanded one that introduced DistroSeriesDifferenceJob. The flag should control creation of those jobs, and thus the tracking of differences between derived distroseries and their respective parents.

== Proposed fix ==

The hard part is testing database permissions for the scripts that may create the new jobs, when most of the tests will run with job generation disabled.

That's why you'll see a test that assumes each of the known database roles that may want to do this, and at least tries to create a job. That really just tests INSERT privileges, but I'm assuming that it's a reasonable indicator in practice. I don't want the test to get too slow and complicated, especially since the real question is whether I forgot any database roles, not whether I forgot a privilege.

== Pre-implementation notes ==

The naming and setting of the flag gave us some trouble:

 * Its name uses underscores to separate words. An existing related flag used dashes. I informed Julian that the documentation does not allow this, and we'll have to restore consistent naming later.

 * According to William, we now test for boolean flags in python by evaluating them as booleans. This means that the empty string is used to signify "false" and any other string (including "false"!) means "true."

== Implementation details ==

I kept the name of the feature flag in a variable. But it's duplicated in flags.py because nobody else seemed to follow that practice there. I suppose it's convenient to be able to grep for the flag name there, though with the dash/underscore problem it is a tradeoff for convenience over safety.

== Tests ==

{{{
./bin/test -vvc lp.soyuz.tests.test_distroseriesdifferencejob
}}}

== Demo and Q/A ==

It should still be possible to publish source package releases, with or without the new feature flag set.

= Launchpad lint =

The warnings about security.cfg are nonsensical; I wish we could get rid of them somehow. The other ones strike me as dubious as well, so I didn't try to "fix" them. (I didn't cause them either).

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/soyuz/interfaces/distroseriesdifferencejob.py
  lib/lp/soyuz/model/distroseriesdifferencejob.py
  lib/lp/soyuz/model/distributionjob.py
  lib/lp/registry/tests/test_distroseries.py
  lib/lp/soyuz/model/publishing.py
  database/schema/security.cfg
  lib/lp/registry/interfaces/distroseries.py
  lib/lp/soyuz/configure.zcml
  lib/lp/soyuz/interfaces/distributionjob.py
  lib/lp/soyuz/tests/test_distroseriesdifferencejob.py
  lib/lp/registry/model/distroseries.py
  lib/canonical/launchpad/interfaces/_schema_circular_imports.py

./database/schema/security.cfg
     735: Line exceeds 78 characters.
     736: Line exceeds 78 characters.
     737: Line exceeds 78 characters.
     763: Line exceeds 78 characters.
     767: Line exceeds 78 characters.
     822: Line exceeds 78 characters.
     836: Line exceeds 78 characters.
     837: Line exceeds 78 characters.
     854: Line exceeds 78 characters.
     855: Line exceeds 78 characters.
     856: Line exceeds 78 characters.
     857: Line exceeds 78 characters.
     858: Line exceeds 78 characters.
     911: Line exceeds 78 characters.
     912: Line exceeds 78 characters.
     913: Line exceeds 78 characters.
     943: Line exceeds 78 characters.
    1024: Line exceeds 78 characters.
    1034: Line exceeds 78 characters.
    1035: Line exceeds 78 characters.
./lib/lp/registry/interfaces/distroseries.py
     430: E301 expected 1 blank line, found 2
     471: E301 expected 1 blank line, found 0
./lib/lp/registry/model/distroseries.py
     403: E301 expected 1 blank line, found 2

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

(14:12:05) adeuring: jtv: the branch looks good. only one detail: FEATURE_FLAG as a variable name does not tell what the feature is about. Could you rename it to something like ENABLE_DERIVED_SERIES_JOBS ?
(14:12:36) jtv: adeuring: I'm in two minds about that… doesn't the module it's in do enough to disambiguate it?
(14:12:55) jtv: I could import it into the test as distroseriesdifferencejob.FEATURE_FLAG
(14:13:02) adeuring: jtv: well, we could have two feature flags in this module
(14:13:05) jtv: That way the name is always disambiguated.
(14:13:28) jtv: Ah I see what you mean.
(14:14:41) adeuring: I prefer names which tell what is done over how things are done
(14:15:05) jtv: Okay, then how about ENABLING_FEATURE_FLAG?
(14:15:24) jtv: Because it is important to my mind that it is the feature flag, not e.g. a boolean that controls the feature.
(14:15:54) jtv: If the test refers to it as distroseriesdifferencejob.ENABLING_FEATURE_FLAG, I think that'd be about as clear as it could be.
(14:16:01) adeuring: jtv: what about FEATURE_FLAG_ENABLE_DERIVED_JOBS?
(14:16:28) jtv: Gets a bit long, and basically for the purpose of repeating the module name.
(14:16:49) adeuring: without say what is enabled, it looks to me still like a label on an electrial switch just saying "SWITCH" ;)
(14:18:03) jtv: Well that would make sense in the context of a little circuit board when you know what the board is for, just not what outside part should be attached to the particular piece of wire you're looking at.
(14:18:57) adeuring: jtv, ok, so... ENABLE_MODULE? or FLAG_ENALBE_MODULE?
(14:19:18) jtv: It would have to say "flag," but I don't see why it should say "module."
(14:19:58) adeuring: well, I still think that its good to indicate what is enabled
(14:21:31) jtv: Yes, but I'm relying on the "distroseriesdifferencejob" to imply that it's DistroSeriesDifferenceJob that's being enabled.
(14:22:13) jtv: I don't think it'll confuse anyone, especially since it's not even being exported—it's just the test that refers to it from the outside.
(14:23:42) adeuring: jtv: ok, for the test distroseriesdifferencejob.WHAETEVER is more or less fine, but _inside_ the module something like FEATURE_FLAG_ENABLE_MODULE would still make more sense to me
(14:25:47) jtv: OK, I can't say I like it much but you have the advantage of a fresh outside look. I'll use that.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/services/features/flags.py'
2--- lib/lp/services/features/flags.py 2011-02-25 15:53:47 +0000
3+++ lib/lp/services/features/flags.py 2011-03-11 13:38:10 +0000
4@@ -73,6 +73,10 @@
5 'boolean',
6 'Enables derivative distributions pages.',
7 ''),
8+ ('soyuz.derived_series_jobs.enabled',
9+ 'boolean',
10+ "Compute package differences for derived distributions.",
11+ ''),
12 ('translations.sharing_information.enabled',
13 'boolean',
14 'Enables display of sharing information on translation pages.',
15
16=== modified file 'lib/lp/soyuz/model/distroseriesdifferencejob.py'
17--- lib/lp/soyuz/model/distroseriesdifferencejob.py 2011-03-11 13:38:09 +0000
18+++ lib/lp/soyuz/model/distroseriesdifferencejob.py 2011-03-11 13:38:10 +0000
19@@ -14,6 +14,7 @@
20 )
21
22 from canonical.launchpad.interfaces.lpstorm import IMasterStore
23+from lp.services.features import getFeatureFlag
24 from lp.services.job.model.job import Job
25 from lp.soyuz.interfaces.distributionjob import (
26 DistributionJobType,
27@@ -28,6 +29,9 @@
28 )
29
30
31+FEATURE_FLAG_ENABLE_MODULE = u"soyuz.derived_series_jobs.enabled"
32+
33+
34 def make_metadata(sourcepackagename):
35 """Return JSON metadata for a job on `sourcepackagename`."""
36 return {'sourcepackagename': sourcepackagename.id}
37@@ -102,6 +106,8 @@
38 @classmethod
39 def createForPackagePublication(cls, distroseries, sourcepackagename):
40 """See `IDistroSeriesDifferenceJobSource`."""
41+ if not getFeatureFlag(FEATURE_FLAG_ENABLE_MODULE):
42+ return
43 children = distroseries.getDerivedSeries()
44 parent = distroseries.parent_series
45 for relative in list(children) + [parent]:
46
47=== modified file 'lib/lp/soyuz/tests/test_distroseriesdifferencejob.py'
48--- lib/lp/soyuz/tests/test_distroseriesdifferencejob.py 2011-03-11 13:38:09 +0000
49+++ lib/lp/soyuz/tests/test_distroseriesdifferencejob.py 2011-03-11 13:38:10 +0000
50@@ -5,16 +5,23 @@
51
52 __metaclass__ = type
53
54+import transaction
55+from psycopg2 import ProgrammingError
56 from zope.component import getUtility
57 from zope.interface.verify import verifyObject
58
59-from canonical.testing.layers import ZopelessDatabaseLayer
60+from canonical.testing.layers import (
61+ LaunchpadZopelessLayer,
62+ ZopelessDatabaseLayer,
63+ )
64+from lp.services.features.testing import FeatureFixture
65 from lp.services.job.interfaces.job import JobStatus
66 from lp.soyuz.interfaces.distroseriesdifferencejob import (
67 IDistroSeriesDifferenceJobSource,
68 )
69 from lp.soyuz.model.distroseriesdifferencejob import (
70 create_job,
71+ FEATURE_FLAG_ENABLE_MODULE,
72 find_waiting_jobs,
73 make_metadata,
74 may_require_job,
75@@ -27,6 +34,10 @@
76
77 layer = ZopelessDatabaseLayer
78
79+ def setUp(self):
80+ super(TestDistroSeriesDifferenceJobSource, self).setUp()
81+ self.useFixture(FeatureFixture({FEATURE_FLAG_ENABLE_MODULE: u'on'}))
82+
83 def getJobSource(self):
84 return getUtility(IDistroSeriesDifferenceJobSource)
85
86@@ -144,3 +155,41 @@
87 jobs = list(find_waiting_jobs(derived_series, package))
88 self.assertEqual(1, len(jobs))
89 self.assertEqual(package.id, jobs[0].metadata['sourcepackagename'])
90+
91+ def test_createForPackagePublication_obeys_feature_flag(self):
92+ distroseries = self.makeDerivedDistroSeries()
93+ package = self.factory.makeSourcePackageName()
94+ self.useFixture(FeatureFixture({FEATURE_FLAG_ENABLE_MODULE: ''}))
95+ self.getJobSource().createForPackagePublication(distroseries, package)
96+ self.assertContentEqual([], find_waiting_jobs(distroseries, package))
97+
98+
99+class TestDistroSeriesDifferenceJobPermissions(TestCaseWithFactory):
100+ """Database permissions test for `DistroSeriesDifferenceJob`."""
101+
102+ layer = LaunchpadZopelessLayer
103+
104+ def test_permissions(self):
105+ script_users = [
106+ 'archivepublisher',
107+ 'queued',
108+ 'uploader',
109+ ]
110+ derived_series = self.factory.makeDistroSeries(
111+ parent_series=self.factory.makeDistroSeries())
112+ packages = dict(
113+ (user, self.factory.makeSourcePackageName())
114+ for user in script_users)
115+ transaction.commit()
116+ for user in script_users:
117+ self.layer.switchDbUser(user)
118+ try:
119+ create_job(derived_series, packages[user])
120+ except ProgrammingError, e:
121+ self.assertTrue(
122+ False,
123+ "Database role %s was unable to create a job. "
124+ "Error was: %s" % (user, e))
125+
126+ # The test is that we get here without exceptions.
127+ pass