Merge lp:~sinzui/launchpad/commercial-jobsource-zcml into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Approved by: Curtis Hovey
Approved revision: no longer in the source branch.
Merged at revision: 15422
Proposed branch: lp:~sinzui/launchpad/commercial-jobsource-zcml
Merge into: lp:launchpad
Diff against target: 219 lines (+95/-8)
7 files modified
cronscripts/daily_product_jobs.py (+1/-1)
database/schema/security.cfg (+6/-0)
lib/lp/registry/configure.zcml (+31/-0)
lib/lp/registry/model/productjob.py (+3/-0)
lib/lp/registry/tests/test_productjob.py (+52/-3)
lib/lp/services/config/schema-lazr.conf (+2/-2)
logs/README.txt (+0/-2)
To merge this branch: bzr merge lp:~sinzui/launchpad/commercial-jobsource-zcml
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Review via email: mp+110365@code.launchpad.net

Commit message

Register the commercial email job sources and add db permissions.

Description of the change

process-job-source.py fails because the commercial notification classes
are not registered in ZCML. Clearly, a test is missing that demonstrates
the ZCML, schema-lazr.conf, and security.cfg are configured properly.

I really botched this. I new what test was missing when I discovered the
bug. There are two cronscripts used in this process, and I only wrote
tests for the script I wrote. I need to also write a test for the script
I reused.

--------------------------------------------------------------------

RULES

    Pre-implementation: no one
    * Add the missing ZCML needed to register the secured utilities.
    * Add tests to verify the three kinds of job are run.
      * Fix any db permissions that are proven to fail.
    * Fix the ICommercialExpiredJob to be ICommercialExpiredJobSource
      in the schema.

QA

    On qastaging, as a webops to run
    * cronscripts/process-job-source.py ICommercialExpiredJobSource
    * cronscripts/process-job-source.py ISevenDayCommercialExpirationJobSource
    * cronscripts/process-job-source.py IThirtyDayCommercialExpirationJobSource

LINT

    cronscripts/daily_product_jobs.py
    database/schema/security.cfg
    lib/lp/registry/configure.zcml
    lib/lp/registry/model/productjob.py
    lib/lp/registry/tests/test_productjob.py
    lib/lp/services/config/schema-lazr.conf

TEST

    ./bin/test -vvc lp.registry.tests.test_productjob

IMPLEMENTATION

Fixed the class name used to lookup jobs
ICommercialExpiredJob => ICommercialExpiredJobSource
    cronscripts/daily_product_jobs.py
    lib/lp/services/config/schema-lazr.conf

Added a test to the test mixin to verify that the three kinds of
commercial notification job can be run by process-job-source.py. I
discovered that there is two run_script() test helpers with different
IO. I chose to use update an existing test to use the one from
lp.testing because it allows me to pass the environ to get better test
output.
    lib/lp/registry/model/productjob.py
    lib/lp/registry/tests/test_productjob.py

Added the missing ZCML registration that permits process-job-source.py
to lookup the job source class and then have permission to use the job
instance.
    lib/lp/registry/configure.zcml

Fixed the db permissions demonstrated to be need by the new test. The
packaging/distro tables are needed because there is a sanity check in
the product deactivation code that ensure Ubuntu does not loose an
upstream packaging link
    database/schema/security.cfg

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

Curtis--

This looks fine; I'm a little confused about the block of lp.services imports being moved--is there some sort of interference if they're sorted normally?

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cronscripts/daily_product_jobs.py'
2--- cronscripts/daily_product_jobs.py 2012-06-07 19:14:04 +0000
3+++ cronscripts/daily_product_jobs.py 2012-06-14 16:53:21 +0000
4@@ -21,7 +21,7 @@
5
6 def __init__(self):
7 name = 'daily_product_jobs'
8- dbuser = config.ICommercialExpiredJob.dbuser
9+ dbuser = config.ICommercialExpiredJobSource.dbuser
10 LaunchpadCronScript.__init__(self, name, dbuser)
11
12 def main(self):
13
14=== modified file 'database/schema/security.cfg'
15--- database/schema/security.cfg 2012-06-08 15:46:51 +0000
16+++ database/schema/security.cfg 2012-06-14 16:53:21 +0000
17@@ -2367,11 +2367,17 @@
18 [product-job]
19 groups=script
20 public.account = SELECT
21+public.branch = SELECT
22 public.commercialsubscription = SELECT, UPDATE, DELETE
23+public.distribution = SELECT
24+public.distroseries = SELECT
25 public.emailaddress = SELECT
26 public.job = SELECT, INSERT, UPDATE
27 public.person = SELECT
28+public.packaging = SELECT
29 public.product = SELECT, UPDATE
30+public.productlicense = SELECT
31 public.productseries = SELECT, UPDATE
32 public.productjob = SELECT, INSERT
33+public.sourcepackagename = SELECT
34 type=user
35
36=== modified file 'lib/lp/registry/configure.zcml'
37--- lib/lp/registry/configure.zcml 2012-05-29 03:59:16 +0000
38+++ lib/lp/registry/configure.zcml 2012-06-14 16:53:21 +0000
39@@ -110,6 +110,37 @@
40 <allow interface=".interfaces.persontransferjob.IPersonMergeJob"/>
41 </class>
42
43+ <!-- IProductNotificationJob -->
44+ <securedutility
45+ component=".model.productjob.CommercialExpiredJob"
46+ provides=".interfaces.productjob.ICommercialExpiredJobSource">
47+ <allow interface=".interfaces.productjob.ICommercialExpiredJobSource"/>
48+ </securedutility>
49+
50+ <class class=".model.productjob.CommercialExpiredJob">
51+ <allow interface=".interfaces.productjob.ICommercialExpiredJob"/>
52+ </class>
53+
54+ <securedutility
55+ component=".model.productjob.SevenDayCommercialExpirationJob"
56+ provides=".interfaces.productjob.ISevenDayCommercialExpirationJobSource">
57+ <allow interface=".interfaces.productjob.ISevenDayCommercialExpirationJobSource"/>
58+ </securedutility>
59+
60+ <class class=".model.productjob.SevenDayCommercialExpirationJob">
61+ <allow interface=".interfaces.productjob.ISevenDayCommercialExpirationJob"/>
62+ </class>
63+
64+ <securedutility
65+ component=".model.productjob.ThirtyDayCommercialExpirationJob"
66+ provides=".interfaces.productjob.IThirtyDayCommercialExpirationJobSource">
67+ <allow interface=".interfaces.productjob.IThirtyDayCommercialExpirationJobSource"/>
68+ </securedutility>
69+
70+ <class class=".model.productjob.ThirtyDayCommercialExpirationJob">
71+ <allow interface=".interfaces.productjob.IThirtyDayCommercialExpirationJob"/>
72+ </class>
73+
74 <!-- INameBlacklist -->
75 <securedutility
76 class="lp.registry.model.nameblacklist.NameBlacklistSet"
77
78=== modified file 'lib/lp/registry/model/productjob.py'
79--- lib/lp/registry/model/productjob.py 2012-06-14 05:18:22 +0000
80+++ lib/lp/registry/model/productjob.py 2012-06-14 16:53:21 +0000
81@@ -77,6 +77,7 @@
82 simple_sendmail,
83 )
84 from lp.services.propertycache import cachedproperty
85+from lp.services.scripts import log
86 from lp.services.webapp.publisher import canonical_url
87
88
89@@ -332,6 +333,8 @@
90 body, headers = self.getBodyAndHeaders(
91 email_template, address, self.reply_to)
92 simple_sendmail(from_address, address, subject, body, headers)
93+ log.debug("%s has sent email to the maintainer of %s.",
94+ self.log_name, self.product.name)
95
96 def run(self):
97 """See `BaseRunnableJob`.
98
99=== modified file 'lib/lp/registry/tests/test_productjob.py'
100--- lib/lp/registry/tests/test_productjob.py 2012-06-14 05:18:22 +0000
101+++ lib/lp/registry/tests/test_productjob.py 2012-06-14 16:53:21 +0000
102@@ -12,6 +12,8 @@
103
104 import pytz
105 import transaction
106+from testtools.content import Content
107+from testtools.content_type import UTF8_TEXT
108 from zope.component import getUtility
109 from zope.interface import (
110 classProvides,
111@@ -47,12 +49,14 @@
112 SevenDayCommercialExpirationJob,
113 ThirtyDayCommercialExpirationJob,
114 )
115+from lp.services.database.lpstorm import IStore
116+from lp.services.job.interfaces.job import JobStatus
117 from lp.services.log.logger import BufferLogger
118 from lp.services.propertycache import clear_property_cache
119-from lp.services.scripts.tests import run_script
120 from lp.services.webapp.publisher import canonical_url
121 from lp.testing import (
122 person_logged_in,
123+ run_script,
124 TestCaseWithFactory,
125 )
126 from lp.testing.layers import (
127@@ -147,8 +151,11 @@
128 # ProductJobManagerTestCase.test_createAllDailyJobs
129 self.make_test_products()
130 transaction.commit()
131- retcode, stdout, stderr = run_script(
132- 'cronscripts/daily_product_jobs.py', [])
133+ stdout, stderr, retcode = run_script(
134+ 'cronscripts/daily_product_jobs.py')
135+ self.addDetail("stdout", Content(UTF8_TEXT, lambda: stdout))
136+ self.addDetail("stderr", Content(UTF8_TEXT, lambda: stderr))
137+ self.assertEqual(0, retcode)
138 self.assertIn('Requested 3 total product jobs.', stderr)
139
140
141@@ -556,6 +563,48 @@
142 self.assertEqual(1, len(notifications))
143 self.assertIn(iso_date, notifications[0].get_payload())
144
145+ def test_run_cronscript(self):
146+ # Everything is configured: ZCML, schema-lazr.conf, and security.cfg.
147+ product, reviewer = self.make_notification_data()
148+ private_branch = self.factory.makeBranch(
149+ owner=product.owner, product=product,
150+ information_type=InformationType.USERDATA)
151+ with person_logged_in(product.owner):
152+ product.setPrivateBugs(True, product.owner)
153+ product.development_focus.branch = private_branch
154+ self.expire_commercial_subscription(product)
155+ job = self.JOB_CLASS.create(product, reviewer)
156+ # Create a proprietary project which will have different DB relations.
157+ proprietary_product = self.factory.makeProduct(
158+ licenses=[License.OTHER_PROPRIETARY])
159+ self.expire_commercial_subscription(proprietary_product)
160+ proprietary_job = self.JOB_CLASS.create(proprietary_product, reviewer)
161+ transaction.commit()
162+
163+ out, err, exit_code = run_script(
164+ "LP_DEBUG_SQL=1 cronscripts/process-job-source.py -vv %s" %
165+ self.JOB_SOURCE_INTERFACE.getName())
166+ self.addDetail("stdout", Content(UTF8_TEXT, lambda: out))
167+ self.addDetail("stderr", Content(UTF8_TEXT, lambda: err))
168+ self.assertEqual(0, exit_code)
169+ self.assertTrue(
170+ 'Traceback (most recent call last)' not in err)
171+ message = (
172+ '%s has sent email to the maintainer of %s.' % (
173+ self.JOB_CLASS.__name__, product.name))
174+ self.assertTrue(
175+ message in err,
176+ 'Cound not find "%s" in err log:\n%s.' % (message, err))
177+ message = (
178+ '%s has sent email to the maintainer of %s.' % (
179+ self.JOB_CLASS.__name__, proprietary_product.name))
180+ self.assertTrue(
181+ message in err,
182+ 'Cound not find "%s" in err log:\n%s.' % (message, err))
183+ IStore(job.job).invalidate()
184+ self.assertEqual(JobStatus.COMPLETED, job.job.status)
185+ self.assertEqual(JobStatus.COMPLETED, proprietary_job.job.status)
186+
187
188 class SevenDayCommercialExpirationJobTestCase(CommericialExpirationMixin,
189 TestCaseWithFactory):
190
191=== modified file 'lib/lp/services/config/schema-lazr.conf'
192--- lib/lp/services/config/schema-lazr.conf 2012-06-13 01:05:42 +0000
193+++ lib/lp/services/config/schema-lazr.conf 2012-06-14 16:53:21 +0000
194@@ -1749,7 +1749,7 @@
195 # dbuser, the crontab_group, and the module that the job source class
196 # can be loaded from.
197 job_sources:
198- ICommercialExpiredJob,
199+ ICommercialExpiredJobSource,
200 IMembershipNotificationJobSource,
201 IPersonMergeJobSource,
202 IPlainPackageCopyJobSource,
203@@ -1759,7 +1759,7 @@
204 ISevenDayCommercialExpirationJobSource,
205 IThirtyDayCommercialExpirationJobSource
206
207-[ICommercialExpiredJob]
208+[ICommercialExpiredJobSource]
209 module: lp.registry.interfaces.productjob
210 dbuser: product-job
211 crontab_group: MAIN
212
213=== removed directory 'logs'
214=== removed file 'logs/README.txt'
215--- logs/README.txt 2012-06-14 13:41:58 +0000
216+++ logs/README.txt 1970-01-01 00:00:00 +0000
217@@ -1,2 +0,0 @@
218-This directory is included in revision control so that it exists for
219-tests that need to write logs.