Merge lp:~abentley/charmworld/remove-scan-ingest-job into lp:~juju-jitsu/charmworld/trunk
- remove-scan-ingest-job
- Merge into trunk
Proposed by
Aaron Bentley
Status: | Merged |
---|---|
Approved by: | Curtis Hovey |
Approved revision: | 285 |
Merged at revision: | 281 |
Proposed branch: | lp:~abentley/charmworld/remove-scan-ingest-job |
Merge into: | lp:~juju-jitsu/charmworld/trunk |
Prerequisite: | lp:~abentley/charmworld/remove-bzr-ingest-job |
Diff against target: |
409 lines (+152/-150) 3 files modified
charmworld/jobs/ingest.py (+134/-134) charmworld/jobs/tests/test_scan.py (+16/-14) charmworld/testing/factory.py (+2/-2) |
To merge this branch: | bzr merge lp:~abentley/charmworld/remove-scan-ingest-job |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Curtis Hovey (community) | code | Approve | |
Review via email: mp+171093@code.launchpad.net |
Commit message
Remove ScanIngestJob
Description of the change
This branch removes ScanIngestJob, as part of the fix for 1190015: "ingest is really a set of functions that are run consecutively, but is structured as a list of jobs".
To post a comment you must log in.
Revision history for this message
Aaron Bentley (abentley) wrote : | # |
I believe the i_requires and i_provides fields were meant to make these easy to query for a search engine ("i" being short for "index"). We should probably wait until we've restructured the representation of "requires" and "provides" before we consider deleting the i_ variants.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'charmworld/jobs/ingest.py' |
2 | --- charmworld/jobs/ingest.py 2013-06-21 19:38:25 +0000 |
3 | +++ charmworld/jobs/ingest.py 2013-06-24 14:42:35 +0000 |
4 | @@ -244,8 +244,7 @@ |
5 | if not run_job(ChangelogIngestJob(), charm_data): |
6 | return False |
7 | update_date_created(charm_data, log) |
8 | - if not run_job(ScanIngestJob(), charm_data, db=db): |
9 | - return False |
10 | + scan_charm(charm_data, db=db) |
11 | except Exception as e: |
12 | err_msg = "%s error: %s" % (charm_data, str(e)) |
13 | log.exception(err_msg) |
14 | @@ -509,140 +508,141 @@ |
15 | charm['proof'] = proof |
16 | |
17 | |
18 | -class ScanIngestJob(FSIngestJob): |
19 | - |
20 | - name = 'scan' |
21 | - |
22 | - @staticmethod |
23 | - def normalize_interfaces(interfaces): |
24 | - # Transform the short defintion of relations into the |
25 | - # long form. Quoted from |
26 | - # https://juju.ubuntu.com/docs/charm.html#the-metadata-file : |
27 | - # |
28 | - # As a shortcut, if these properties are not defined, and |
29 | - # instead a single string value is provided next to the |
30 | - # relation name, the string is taken as the interface |
31 | - # value, as seen in this example: |
32 | - # |
33 | - # requires: |
34 | - # db: mysql |
35 | - for relation_name, relation_data in interfaces.items(): |
36 | - if isinstance(relation_data, StringTypes): |
37 | - interfaces[relation_name] = {'interface': relation_data} |
38 | - |
39 | - @classmethod |
40 | - def process_charm(cls, charm): |
41 | - # Enrich charm metadata for webapp. |
42 | - |
43 | - # Charm url |
44 | - if charm["promulgated"]: |
45 | - charm["short_url"] = "/charms/%s/%s" % ( |
46 | - charm["series"], charm["name"]) |
47 | - else: |
48 | - charm["short_url"] = "/~%s/%s/%s" % (charm["owner"], |
49 | - charm["series"], |
50 | - charm["name"]) |
51 | - |
52 | - # Charm label |
53 | - if charm["promulgated"]: |
54 | - charm["label"] = "%s/%s" % (charm["series"], charm["name"]) |
55 | - else: |
56 | - charm["label"] = "~%s:%s/%s" % (charm["owner"], |
57 | - charm["series"], |
58 | - charm["name"]) |
59 | - |
60 | - # Flatten the interfaces provided |
61 | - i_provides = [] |
62 | - provides = charm.get("provides") |
63 | - if provides: |
64 | - cls.normalize_interfaces(provides) |
65 | - for v in provides.values(): |
66 | - if not isinstance(v, dict): |
67 | - continue |
68 | - i = v.get("interface") |
69 | - if not i: |
70 | - continue |
71 | - i_provides.append(i) |
72 | - charm["i_provides"] = i_provides |
73 | - |
74 | - # Flatten the interfaces required |
75 | - i_requires = [] |
76 | - requires = charm.get("requires") |
77 | - if requires: |
78 | - cls.normalize_interfaces(requires) |
79 | - for v in requires.values(): |
80 | - i = v.get("interface") |
81 | - if not i: |
82 | - continue |
83 | - i_requires.append(i) |
84 | - charm["i_requires"] = i_requires |
85 | - return charm |
86 | - |
87 | - def run(self, charm_data): |
88 | - # Note: charm_data is modified in-place. IndexIngestJob requires |
89 | - # these modifications. |
90 | - files = charm_data['files'] |
91 | - # Some files have bad characters in them since they are used as mongo |
92 | - # keys. Use their escaped forms instead. |
93 | - metadata_file = quote_key('metadata.yaml') |
94 | - config_file = quote_key('config.yaml') |
95 | - |
96 | - if metadata_file not in files: |
97 | - self.log.info( |
98 | - "Charm has no metadata: %s", charm_data["branch_spec"]) |
99 | - return |
100 | - else: |
101 | - cfile = CharmFileSet.get_by_id( |
102 | - self.fs, files[metadata_file]['fileid']) |
103 | - try: |
104 | - metadata = quote_yaml(yaml.load(cfile.read())) |
105 | - except Exception, exc: |
106 | - raise IngestError( |
107 | - 'Invalid charm metadata %s: %s' % ( |
108 | +def scan_charm(charm_data, db=None, fs=None): |
109 | + # Note: charm_data is modified in-place. IndexIngestJob requires |
110 | + # these modifications. |
111 | + job = FSIngestJob() |
112 | + job.setup(db, fs) |
113 | + db = job.db |
114 | + fs = job.fs |
115 | + log = job.log |
116 | + files = charm_data['files'] |
117 | + # Some files have bad characters in them since they are used as mongo |
118 | + # keys. Use their escaped forms instead. |
119 | + metadata_file = quote_key('metadata.yaml') |
120 | + config_file = quote_key('config.yaml') |
121 | + |
122 | + if metadata_file not in files: |
123 | + log.info( |
124 | + "Charm has no metadata: %s", charm_data["branch_spec"]) |
125 | + return |
126 | + else: |
127 | + cfile = CharmFileSet.get_by_id( |
128 | + fs, files[metadata_file]['fileid']) |
129 | + try: |
130 | + metadata = quote_yaml(yaml.load(cfile.read())) |
131 | + except Exception, exc: |
132 | + raise IngestError( |
133 | + 'Invalid charm metadata %s: %s' % ( |
134 | + charm_data['branch_spec'], |
135 | + exc) |
136 | + ) |
137 | + |
138 | + if config_file in files: |
139 | + cfile = CharmFileSet.get_by_id( |
140 | + fs, files[config_file]['fileid']) |
141 | + config_raw = cfile.read() |
142 | + |
143 | + try: |
144 | + config = quote_yaml(yaml.load(config_raw)) |
145 | + except Exception, exc: |
146 | + raise IngestError( |
147 | + 'Invalid charm config yaml. %s: %s' % ( |
148 | charm_data['branch_spec'], |
149 | exc) |
150 | - ) |
151 | - |
152 | - if config_file in files: |
153 | - cfile = CharmFileSet.get_by_id( |
154 | - self.fs, files[config_file]['fileid']) |
155 | - config_raw = cfile.read() |
156 | - |
157 | - try: |
158 | - config = quote_yaml(yaml.load(config_raw)) |
159 | - except Exception, exc: |
160 | - raise IngestError( |
161 | - 'Invalid charm config yaml. %s: %s' % ( |
162 | - charm_data['branch_spec'], |
163 | - exc) |
164 | - ) |
165 | - |
166 | - metadata["config"] = config |
167 | - metadata["config_raw"] = config_raw |
168 | - |
169 | - if 'revision' in files: |
170 | - cfile = CharmFileSet.get_by_id( |
171 | - self.fs, files['revision']['fileid']) |
172 | - rev_raw = cfile.read() |
173 | - rev_id = int(rev_raw.strip()) |
174 | - metadata["revision"] = rev_id |
175 | - elif not "revision" in metadata: |
176 | - self.log.info("Invalid revision %s", charm_data["branch_spec"]) |
177 | - metadata["revision"] = 0 |
178 | - |
179 | - hooks = [] |
180 | - for filedata in files.values(): |
181 | - if filedata['subdir'] == 'hooks': |
182 | - hooks.append(filedata['filename']) |
183 | - hooks.sort() |
184 | - metadata["hooks"] = hooks |
185 | - |
186 | - # Stuff into the db |
187 | - metadata.update(charm_data) |
188 | - metadata = self.process_charm(metadata) |
189 | - # Modify charm_data itself. |
190 | - charm_data.clear() |
191 | - charm_data.update(metadata) |
192 | + ) |
193 | + |
194 | + metadata["config"] = config |
195 | + metadata["config_raw"] = config_raw |
196 | + |
197 | + if 'revision' in files: |
198 | + cfile = CharmFileSet.get_by_id( |
199 | + fs, files['revision']['fileid']) |
200 | + rev_raw = cfile.read() |
201 | + rev_id = int(rev_raw.strip()) |
202 | + metadata["revision"] = rev_id |
203 | + elif not "revision" in metadata: |
204 | + log.info("Invalid revision %s", charm_data["branch_spec"]) |
205 | + metadata["revision"] = 0 |
206 | + |
207 | + hooks = [] |
208 | + for filedata in files.values(): |
209 | + if filedata['subdir'] == 'hooks': |
210 | + hooks.append(filedata['filename']) |
211 | + hooks.sort() |
212 | + metadata["hooks"] = hooks |
213 | + |
214 | + # Stuff into the db |
215 | + metadata.update(charm_data) |
216 | + metadata = process_charm(metadata) |
217 | + # Modify charm_data itself. |
218 | + charm_data.clear() |
219 | + charm_data.update(metadata) |
220 | + |
221 | + |
222 | +def normalize_interfaces(interfaces): |
223 | + # Transform the short defintion of relations into the |
224 | + # long form. Quoted from |
225 | + # https://juju.ubuntu.com/docs/charm.html#the-metadata-file : |
226 | + # |
227 | + # As a shortcut, if these properties are not defined, and |
228 | + # instead a single string value is provided next to the |
229 | + # relation name, the string is taken as the interface |
230 | + # value, as seen in this example: |
231 | + # |
232 | + # requires: |
233 | + # db: mysql |
234 | + for relation_name, relation_data in interfaces.items(): |
235 | + if isinstance(relation_data, StringTypes): |
236 | + interfaces[relation_name] = {'interface': relation_data} |
237 | + |
238 | + |
239 | +def process_charm(charm): |
240 | + # Enrich charm metadata for webapp. |
241 | + |
242 | + # Charm url |
243 | + if charm["promulgated"]: |
244 | + charm["short_url"] = "/charms/%s/%s" % ( |
245 | + charm["series"], charm["name"]) |
246 | + else: |
247 | + charm["short_url"] = "/~%s/%s/%s" % (charm["owner"], |
248 | + charm["series"], |
249 | + charm["name"]) |
250 | + |
251 | + # Charm label |
252 | + if charm["promulgated"]: |
253 | + charm["label"] = "%s/%s" % (charm["series"], charm["name"]) |
254 | + else: |
255 | + charm["label"] = "~%s:%s/%s" % (charm["owner"], |
256 | + charm["series"], |
257 | + charm["name"]) |
258 | + |
259 | + # Flatten the interfaces provided |
260 | + i_provides = [] |
261 | + provides = charm.get("provides") |
262 | + if provides: |
263 | + normalize_interfaces(provides) |
264 | + for v in provides.values(): |
265 | + if not isinstance(v, dict): |
266 | + continue |
267 | + i = v.get("interface") |
268 | + if not i: |
269 | + continue |
270 | + i_provides.append(i) |
271 | + charm["i_provides"] = i_provides |
272 | + |
273 | + # Flatten the interfaces required |
274 | + i_requires = [] |
275 | + requires = charm.get("requires") |
276 | + if requires: |
277 | + normalize_interfaces(requires) |
278 | + for v in requires.values(): |
279 | + i = v.get("interface") |
280 | + if not i: |
281 | + continue |
282 | + i_requires.append(i) |
283 | + charm["i_requires"] = i_requires |
284 | + return charm |
285 | |
286 | |
287 | def addresses(charm): |
288 | |
289 | === modified file 'charmworld/jobs/tests/test_scan.py' |
290 | --- charmworld/jobs/tests/test_scan.py 2013-06-21 19:53:57 +0000 |
291 | +++ charmworld/jobs/tests/test_scan.py 2013-06-24 14:42:35 +0000 |
292 | @@ -2,8 +2,10 @@ |
293 | # GNU Affero General Public License version 3 (see the file LICENSE). |
294 | |
295 | from charmworld.jobs.ingest import ( |
296 | + FSIngestJob, |
297 | IngestError, |
298 | - ScanIngestJob, |
299 | + process_charm, |
300 | + scan_charm, |
301 | store_branch_files, |
302 | ) |
303 | from charmworld.testing import factory |
304 | @@ -11,21 +13,21 @@ |
305 | from charmworld.utils import quote_key |
306 | |
307 | |
308 | -class TestScanJob(JobTestBase): |
309 | +class TestScanCharm(JobTestBase): |
310 | |
311 | def setUp(self): |
312 | - super(TestScanJob, self).setUp() |
313 | - self.scan_job = ScanIngestJob() |
314 | - self.scan_job.setup(db=self.db) |
315 | + super(TestScanCharm, self).setUp() |
316 | + self.fs_job = FSIngestJob() |
317 | + self.fs_job.setup(db=self.db) |
318 | ignore, self.charm = factory.makeCharm(self.db, with_real_files=True) |
319 | self.charm['files'] = dict([ |
320 | (quote_key(cfile.filename), dict(cfile)) for cfile in |
321 | - store_branch_files(self.scan_job.fs, self.charm, self.scan_job.log) |
322 | + store_branch_files(self.fs_job.fs, self.charm, self.fs_job.log) |
323 | ]) |
324 | |
325 | def test_config_metadata_with_dotted_keys(self): |
326 | del self.charm['config'] |
327 | - self.scan_job.run(self.charm) |
328 | + scan_charm(self.charm, self.db, self.fs_job.fs) |
329 | # The key 'dotted.name' is replaced with 'dotted%2Ename', and |
330 | # 'foo.bar' is replaced with 'foo%2Ebar'. |
331 | self.assertFalse('dotted.name' in self.charm) |
332 | @@ -42,11 +44,11 @@ |
333 | # https://juju.ubuntu.com/docs/charm.html#the-metadata-file |
334 | # for more details.) |
335 | # |
336 | - # ScanJob.normalize_interfaces() transforms the short form |
337 | + # normalize_interfaces() transforms the short form |
338 | # into the long form for further processing and for storage. |
339 | del self.charm['provides'] |
340 | del self.charm['requires'] |
341 | - self.scan_job.run(self.charm) |
342 | + scan_charm(self.charm, self.db, self.fs_job.fs) |
343 | # The sample metadata file has entries for the short and long |
344 | # form of the interface specification in the "provides" and |
345 | # in the "requires" section. The short entries are converted |
346 | @@ -70,7 +72,7 @@ |
347 | 'fileid': metadata_file._id |
348 | } |
349 | with self.assertRaises(IngestError) as ctx: |
350 | - self.scan_job.run(self.charm) |
351 | + scan_charm(self.charm, self.db, self.fs_job.fs) |
352 | info = str(ctx.exception) |
353 | self.assertTrue(info.startswith( |
354 | 'Invalid charm metadata ~charmers/charms/precise/sample')) |
355 | @@ -83,7 +85,7 @@ |
356 | 'fileid': config_file._id |
357 | } |
358 | with self.assertRaises(IngestError) as ctx: |
359 | - self.scan_job.run(self.charm) |
360 | + scan_charm(self.charm, self.db, self.fs_job.fs) |
361 | info = str(ctx.exception) |
362 | self.assertTrue(info.startswith( |
363 | 'Invalid charm config yaml. ~charmers/charms/precise/sample')) |
364 | @@ -97,7 +99,7 @@ |
365 | 'name': 'bar', |
366 | 'promulgated': False, |
367 | } |
368 | - self.scan_job.process_charm(charm) |
369 | + process_charm(charm) |
370 | self.assertEqual('/~foo/precise/bar', charm['short_url']) |
371 | # The label of unpromulgated charms starts with the owner name. |
372 | self.assertEqual('~foo:precise/bar', charm['label']) |
373 | @@ -109,7 +111,7 @@ |
374 | 'name': 'bar', |
375 | 'promulgated': True, |
376 | } |
377 | - self.scan_job.process_charm(charm) |
378 | + process_charm(charm) |
379 | self.assertEqual('/charms/precise/bar', charm['short_url']) |
380 | self.assertEqual('precise/bar', charm['label']) |
381 | # This is also true if the charm is not owned by charmes. |
382 | @@ -119,6 +121,6 @@ |
383 | 'name': 'bar', |
384 | 'promulgated': True, |
385 | } |
386 | - self.scan_job.process_charm(charm) |
387 | + process_charm(charm) |
388 | self.assertEqual('/charms/precise/bar', charm['short_url']) |
389 | self.assertEqual('precise/bar', charm['label']) |
390 | |
391 | === modified file 'charmworld/testing/factory.py' |
392 | --- charmworld/testing/factory.py 2013-06-17 15:02:59 +0000 |
393 | +++ charmworld/testing/factory.py 2013-06-24 14:42:35 +0000 |
394 | @@ -21,7 +21,7 @@ |
395 | ) |
396 | from charmworld.jobs.ingest import ( |
397 | JENKINS_PROVIDERS, |
398 | - ScanIngestJob, |
399 | + process_charm, |
400 | ) |
401 | from charmworld.models import ( |
402 | getfs, |
403 | @@ -279,7 +279,7 @@ |
404 | charm['name'], charm['branch_spec'], status, provider) |
405 | if subordinate: |
406 | charm['subordinate'] = True |
407 | - ScanIngestJob.process_charm(charm) |
408 | + process_charm(charm) |
409 | return charm |
410 | |
411 |
Thank you.
This is fine to land. The diff reminds me of the cruft I was concerned about in the DB. Now that we have a Charm model, I don't think we need short_url, i_provides, i_requires (and their d_ counterparts). I fixed a UI bug recently by changing the model to create the short_url if one is not in the db. I will report a bug about this so that we can discuss which data is not needed in the db.