Merge lp:~hazmat/pyjuju/repository-broken-charms into lp:pyjuju

Proposed by Kapil Thangavelu
Status: Merged
Approved by: Kapil Thangavelu
Approved revision: 477
Merged at revision: 495
Proposed branch: lp:~hazmat/pyjuju/repository-broken-charms
Merge into: lp:pyjuju
Diff against target: 242 lines (+74/-42)
4 files modified
juju/charm/errors.py (+22/-13)
juju/charm/repository.py (+9/-4)
juju/charm/tests/test_errors.py (+1/-1)
juju/charm/tests/test_repository.py (+42/-24)
To merge this branch: bzr merge lp:~hazmat/pyjuju/repository-broken-charms
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+98062@code.launchpad.net

Description of the change

Charm local repositories should always process all their charms for a search.

Changes

  - add catch all handler to repository for unexpected errors (os)
  - rearrange charm exception hierarchy
    - charmnotfound is not a charm structural error
    - metadata errors are charm errors.

https://codereview.appspot.com/5841056/

To post a comment you must log in.
Revision history for this message
Kapil Thangavelu (hazmat) wrote :

Reviewers: mp+98062_code.launchpad.net,

Message:
Please take a look.

Description:
Charm local repositories should always process all their charms for a
search.

Changes

   - add catch all handler to repository for unexpected errors (os)
   - rearrange charm exception hierarchy
     - charmnotfound is not a charm structural error
     - metadata errors are charm errors.

https://code.launchpad.net/~hazmat/juju/repository-broken-charms/+merge/98062

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/5841056/

Affected files:
   A [revision details]
   M juju/charm/errors.py
   M juju/charm/repository.py
   M juju/charm/tests/test_errors.py
   M juju/charm/tests/test_repository.py

Revision history for this message
William Reade (fwereade) wrote :

LGTM

https://codereview.appspot.com/5841056/diff/1/juju/charm/errors.py
File juju/charm/errors.py (right):

https://codereview.appspot.com/5841056/diff/1/juju/charm/errors.py#newcode30
juju/charm/errors.py:30: """Raised when an error in the info file of a
charm is found."""
MetaDataError seems to be missing a test; might be worth adding one,
however trivial it may be; and it also appears to have no methods that
need to exist.

https://codereview.appspot.com/5841056/

Revision history for this message
Kapil Thangavelu (hazmat) wrote :

added md error test, the methods are nesc. else we get charmerror behavior which expects two args, instead of juju error behavior (error string). if md is broken we can't satisify the name arg of the charmerror, since we don't definitively know the name.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'juju/charm/errors.py'
2--- juju/charm/errors.py 2012-02-11 03:34:23 +0000
3+++ juju/charm/errors.py 2012-03-17 16:28:20 +0000
4@@ -1,6 +1,21 @@
5 from juju.errors import CharmError, JujuError
6
7
8+class CharmNotFound(JujuError):
9+ """A charm was not found in the repository."""
10+
11+ # This isn't semantically an error with a charm error, its an error
12+ # even finding the charm.
13+
14+ def __init__(self, repository_path, charm_name):
15+ self.repository_path = repository_path
16+ self.charm_name = charm_name
17+
18+ def __str__(self):
19+ return "Charm '%s' not found in repository %s" % (
20+ self.charm_name, self.repository_path)
21+
22+
23 class CharmURLError(CharmError):
24
25 def __init__(self, url, message):
26@@ -11,21 +26,15 @@
27 return "Bad charm URL %r: %s" % (self.url, self.message)
28
29
30-class CharmNotFound(CharmError):
31- """A charm was not found in the repository."""
32-
33- def __init__(self, repository_path, charm_name):
34- self.repository_path = repository_path
35- self.charm_name = charm_name
36-
37- def __str__(self):
38- return "Charm '%s' not found in repository %s" % (
39- self.charm_name, self.repository_path)
40-
41-
42-class MetaDataError(JujuError):
43+class MetaDataError(CharmError):
44 """Raised when an error in the info file of a charm is found."""
45
46+ def __init__(self, *args):
47+ super(CharmError, self).__init__(*args)
48+
49+ def __str__(self):
50+ return super(CharmError, self).__str__()
51+
52
53 class InvalidCharmHook(CharmError):
54 """A named hook was not found to be valid for the charm."""
55
56=== modified file 'juju/charm/repository.py'
57--- juju/charm/repository.py 2012-02-16 19:19:42 +0000
58+++ juju/charm/repository.py 2012-03-17 16:28:20 +0000
59@@ -9,13 +9,13 @@
60 from twisted.web.client import downloadPage, getPage
61 from twisted.web.error import Error
62
63-from juju.charm.errors import MetaDataError, ServiceConfigError
64 from juju.charm.provider import get_charm_from_path
65 from juju.charm.url import CharmURL
66 from juju.errors import FileNotFound
67 from juju.lib import under
68
69-from .errors import CharmNotFound, CharmError, RepositoryNotFound
70+from .errors import (
71+ CharmNotFound, CharmError, RepositoryNotFound, ServiceConfigValueError)
72
73 log = logging.getLogger("juju.charm")
74
75@@ -59,13 +59,18 @@
76 # Log yaml errors for feedback to developers.
77 log.warning("Charm %r has a YAML error: %s", dentry, e)
78 continue
79- except (ServiceConfigError, MetaDataError), e:
80+ except (CharmError, ServiceConfigValueError), e:
81 # Log invalid config.yaml and metadata.yaml semantic errors
82 log.warning("Charm %r has an error: %r %s", dentry, e, e)
83 continue
84- except CharmError:
85+ except CharmNotFound:
86 # This could just be a random directory/file in the repo
87 continue
88+ except Exception, e:
89+ # Catch all (perms, unknowns, etc)
90+ log.warning(
91+ "Unexpected error while processing %s: %r",
92+ dentry, e)
93
94 def find(self, charm_url):
95 """Find a charm with the given name.
96
97=== modified file 'juju/charm/tests/test_errors.py'
98--- juju/charm/tests/test_errors.py 2012-02-11 03:34:23 +0000
99+++ juju/charm/tests/test_errors.py 2012-03-17 16:28:20 +0000
100@@ -29,7 +29,7 @@
101 self.assertEquals(
102 str(error),
103 "Charm 'cassandra' not found in repository /path")
104- self.assertTrue(isinstance(error, CharmError))
105+ self.assertTrue(isinstance(error, JujuError))
106
107 def test_InvalidCharmHook(self):
108 error = InvalidCharmHook("mysql", "magic-relation-changed")
109
110=== modified file 'juju/charm/tests/test_repository.py'
111--- juju/charm/tests/test_repository.py 2012-03-08 02:50:43 +0000
112+++ juju/charm/tests/test_repository.py 2012-03-17 16:28:20 +0000
113@@ -2,7 +2,6 @@
114 import os
115 import inspect
116 import shutil
117-import tempfile
118
119 from twisted.internet.defer import fail, inlineCallbacks, succeed
120 from twisted.web.error import Error
121@@ -12,6 +11,7 @@
122 from juju.charm.repository import (
123 LocalCharmRepository, RemoteCharmRepository, resolve)
124 from juju.charm.url import CharmURL
125+from juju.charm import provider
126 from juju.errors import CharmError
127 from juju.lib import under
128
129@@ -57,6 +57,25 @@
130 # define repository objects
131 self.repository1 = LocalCharmRepository(self.unbundled_repo_path)
132 self.repository2 = LocalCharmRepository(self.bundled_repo_path)
133+ self.output = self.capture_logging("juju.charm")
134+
135+ def assert_there(self, name, repo, revision, latest_revision=None):
136+ url = self.charm_url(name)
137+ charm = yield repo.find(url)
138+ self.assertEquals(charm.get_revision(), revision)
139+ latest = yield repo.latest(url)
140+ self.assertEquals(latest, latest_revision or revision)
141+
142+ @inlineCallbacks
143+ def assert_not_there(self, name, repo, revision=None):
144+ url = self.charm_url(name)
145+ msg = "Charm 'local:series/%s' not found in repository %s" % (
146+ name, repo.path)
147+ err = yield self.assertFailure(repo.find(url), CharmNotFound)
148+ self.assertEquals(str(err), msg)
149+ if revision is None:
150+ err = yield self.assertFailure(repo.latest(url), CharmNotFound)
151+ self.assertEquals(str(err), msg)
152
153 def charm_url(self, name):
154 return CharmURL.parse("local:series/" + name)
155@@ -74,17 +93,6 @@
156 err = self.assertRaises(RepositoryNotFound, LocalCharmRepository, path)
157 self.assertEquals(str(err), "No repository found at %r" % path)
158
159- @inlineCallbacks
160- def assert_not_there(self, name, repo, revision=None):
161- url = self.charm_url(name)
162- msg = "Charm 'local:series/%s' not found in repository %s" % (
163- name, repo.path)
164- err = yield self.assertFailure(repo.find(url), CharmNotFound)
165- self.assertEquals(str(err), msg)
166- if revision is None:
167- err = yield self.assertFailure(repo.latest(url), CharmNotFound)
168- self.assertEquals(str(err), msg)
169-
170 def test_find_inappropriate_url(self):
171 url = CharmURL.parse("cs:foo/bar")
172 err = self.assertRaises(AssertionError, self.repository1.find, url)
173@@ -93,12 +101,31 @@
174 def test_completely_missing(self):
175 return self.assert_not_there("zebra", self.repository1)
176
177- def test_unkown_files_ignored(self):
178+ def test_unknown_files_ignored(self):
179 self.makeFile(
180 "Foobar",
181 path=os.path.join(self.repository1.path, "series", "zebra"))
182 return self.assert_not_there("zebra", self.repository1)
183
184+ @inlineCallbacks
185+ def test_random_error_logged(self):
186+ get_charm = self.mocker.replace(provider.get_charm_from_path)
187+ get_charm(ANY)
188+ self.mocker.throw(SyntaxError("magic"))
189+ self.mocker.count(0, 3)
190+ self.mocker.replay()
191+
192+ yield self.assertFailure(
193+ self.repository1.find(self.charm_url("zebra")),
194+ CharmNotFound)
195+
196+ self.assertIn(
197+ "Unexpected error while processing",
198+ self.output.getvalue())
199+ self.assertIn(
200+ "SyntaxError('magic',)",
201+ self.output.getvalue())
202+
203 def test_unknown_directories_ignored(self):
204 self.makeDir(
205 path=os.path.join(self.repository1.path, "series", "zebra"))
206@@ -116,10 +143,9 @@
207 revision: 0
208 summary: hola""")
209 fh.close()
210- stream = self.capture_logging("juju.charm")
211 yield self.assertFailure(
212 self.repository1.find(self.charm_url("zebra")), CharmNotFound)
213- output = stream.getvalue()
214+ output = self.output.getvalue()
215 self.assertIn(
216 "Charm 'zebra' has a YAML error", output)
217 self.assertIn(
218@@ -140,9 +166,8 @@
219 revision: 0
220 summary: hola""")
221 fh.close()
222- stream = self.capture_logging("juju.charm")
223 yield self.repository1.find(self.charm_url("sample"))
224- output = stream.getvalue()
225+ output = self.output.getvalue()
226 self.assertIn(
227 "Charm 'mysql' has a YAML error", output)
228 self.assertIn(
229@@ -169,13 +194,6 @@
230 def test_repo_type(self):
231 self.assertEqual(self.repository1.type, "local")
232
233- def assert_there(self, name, repo, revision, latest_revision=None):
234- url = self.charm_url(name)
235- charm = yield repo.find(url)
236- self.assertEquals(charm.get_revision(), revision)
237- latest = yield repo.latest(url)
238- self.assertEquals(latest, latest_revision or revision)
239-
240 @inlineCallbacks
241 def test_success_unbundled(self):
242 yield self.assert_there("sample", self.repository1, 2)

Subscribers

People subscribed via source and target branches

to status/vote changes: