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
=== modified file 'juju/charm/errors.py'
--- juju/charm/errors.py 2012-02-11 03:34:23 +0000
+++ juju/charm/errors.py 2012-03-17 16:28:20 +0000
@@ -1,6 +1,21 @@
1from juju.errors import CharmError, JujuError1from juju.errors import CharmError, JujuError
22
33
4class CharmNotFound(JujuError):
5 """A charm was not found in the repository."""
6
7 # This isn't semantically an error with a charm error, its an error
8 # even finding the charm.
9
10 def __init__(self, repository_path, charm_name):
11 self.repository_path = repository_path
12 self.charm_name = charm_name
13
14 def __str__(self):
15 return "Charm '%s' not found in repository %s" % (
16 self.charm_name, self.repository_path)
17
18
4class CharmURLError(CharmError):19class CharmURLError(CharmError):
520
6 def __init__(self, url, message):21 def __init__(self, url, message):
@@ -11,21 +26,15 @@
11 return "Bad charm URL %r: %s" % (self.url, self.message)26 return "Bad charm URL %r: %s" % (self.url, self.message)
1227
1328
14class CharmNotFound(CharmError):29class MetaDataError(CharmError):
15 """A charm was not found in the repository."""
16
17 def __init__(self, repository_path, charm_name):
18 self.repository_path = repository_path
19 self.charm_name = charm_name
20
21 def __str__(self):
22 return "Charm '%s' not found in repository %s" % (
23 self.charm_name, self.repository_path)
24
25
26class MetaDataError(JujuError):
27 """Raised when an error in the info file of a charm is found."""30 """Raised when an error in the info file of a charm is found."""
2831
32 def __init__(self, *args):
33 super(CharmError, self).__init__(*args)
34
35 def __str__(self):
36 return super(CharmError, self).__str__()
37
2938
30class InvalidCharmHook(CharmError):39class InvalidCharmHook(CharmError):
31 """A named hook was not found to be valid for the charm."""40 """A named hook was not found to be valid for the charm."""
3241
=== modified file 'juju/charm/repository.py'
--- juju/charm/repository.py 2012-02-16 19:19:42 +0000
+++ juju/charm/repository.py 2012-03-17 16:28:20 +0000
@@ -9,13 +9,13 @@
9from twisted.web.client import downloadPage, getPage9from twisted.web.client import downloadPage, getPage
10from twisted.web.error import Error10from twisted.web.error import Error
1111
12from juju.charm.errors import MetaDataError, ServiceConfigError
13from juju.charm.provider import get_charm_from_path12from juju.charm.provider import get_charm_from_path
14from juju.charm.url import CharmURL13from juju.charm.url import CharmURL
15from juju.errors import FileNotFound14from juju.errors import FileNotFound
16from juju.lib import under15from juju.lib import under
1716
18from .errors import CharmNotFound, CharmError, RepositoryNotFound17from .errors import (
18 CharmNotFound, CharmError, RepositoryNotFound, ServiceConfigValueError)
1919
20log = logging.getLogger("juju.charm")20log = logging.getLogger("juju.charm")
2121
@@ -59,13 +59,18 @@
59 # Log yaml errors for feedback to developers.59 # Log yaml errors for feedback to developers.
60 log.warning("Charm %r has a YAML error: %s", dentry, e)60 log.warning("Charm %r has a YAML error: %s", dentry, e)
61 continue61 continue
62 except (ServiceConfigError, MetaDataError), e:62 except (CharmError, ServiceConfigValueError), e:
63 # Log invalid config.yaml and metadata.yaml semantic errors63 # Log invalid config.yaml and metadata.yaml semantic errors
64 log.warning("Charm %r has an error: %r %s", dentry, e, e)64 log.warning("Charm %r has an error: %r %s", dentry, e, e)
65 continue65 continue
66 except CharmError:66 except CharmNotFound:
67 # This could just be a random directory/file in the repo67 # This could just be a random directory/file in the repo
68 continue68 continue
69 except Exception, e:
70 # Catch all (perms, unknowns, etc)
71 log.warning(
72 "Unexpected error while processing %s: %r",
73 dentry, e)
6974
70 def find(self, charm_url):75 def find(self, charm_url):
71 """Find a charm with the given name.76 """Find a charm with the given name.
7277
=== modified file 'juju/charm/tests/test_errors.py'
--- juju/charm/tests/test_errors.py 2012-02-11 03:34:23 +0000
+++ juju/charm/tests/test_errors.py 2012-03-17 16:28:20 +0000
@@ -29,7 +29,7 @@
29 self.assertEquals(29 self.assertEquals(
30 str(error),30 str(error),
31 "Charm 'cassandra' not found in repository /path")31 "Charm 'cassandra' not found in repository /path")
32 self.assertTrue(isinstance(error, CharmError))32 self.assertTrue(isinstance(error, JujuError))
3333
34 def test_InvalidCharmHook(self):34 def test_InvalidCharmHook(self):
35 error = InvalidCharmHook("mysql", "magic-relation-changed")35 error = InvalidCharmHook("mysql", "magic-relation-changed")
3636
=== modified file 'juju/charm/tests/test_repository.py'
--- juju/charm/tests/test_repository.py 2012-03-08 02:50:43 +0000
+++ juju/charm/tests/test_repository.py 2012-03-17 16:28:20 +0000
@@ -2,7 +2,6 @@
2import os2import os
3import inspect3import inspect
4import shutil4import shutil
5import tempfile
65
7from twisted.internet.defer import fail, inlineCallbacks, succeed6from twisted.internet.defer import fail, inlineCallbacks, succeed
8from twisted.web.error import Error7from twisted.web.error import Error
@@ -12,6 +11,7 @@
12from juju.charm.repository import (11from juju.charm.repository import (
13 LocalCharmRepository, RemoteCharmRepository, resolve)12 LocalCharmRepository, RemoteCharmRepository, resolve)
14from juju.charm.url import CharmURL13from juju.charm.url import CharmURL
14from juju.charm import provider
15from juju.errors import CharmError15from juju.errors import CharmError
16from juju.lib import under16from juju.lib import under
1717
@@ -57,6 +57,25 @@
57 # define repository objects57 # define repository objects
58 self.repository1 = LocalCharmRepository(self.unbundled_repo_path)58 self.repository1 = LocalCharmRepository(self.unbundled_repo_path)
59 self.repository2 = LocalCharmRepository(self.bundled_repo_path)59 self.repository2 = LocalCharmRepository(self.bundled_repo_path)
60 self.output = self.capture_logging("juju.charm")
61
62 def assert_there(self, name, repo, revision, latest_revision=None):
63 url = self.charm_url(name)
64 charm = yield repo.find(url)
65 self.assertEquals(charm.get_revision(), revision)
66 latest = yield repo.latest(url)
67 self.assertEquals(latest, latest_revision or revision)
68
69 @inlineCallbacks
70 def assert_not_there(self, name, repo, revision=None):
71 url = self.charm_url(name)
72 msg = "Charm 'local:series/%s' not found in repository %s" % (
73 name, repo.path)
74 err = yield self.assertFailure(repo.find(url), CharmNotFound)
75 self.assertEquals(str(err), msg)
76 if revision is None:
77 err = yield self.assertFailure(repo.latest(url), CharmNotFound)
78 self.assertEquals(str(err), msg)
6079
61 def charm_url(self, name):80 def charm_url(self, name):
62 return CharmURL.parse("local:series/" + name)81 return CharmURL.parse("local:series/" + name)
@@ -74,17 +93,6 @@
74 err = self.assertRaises(RepositoryNotFound, LocalCharmRepository, path)93 err = self.assertRaises(RepositoryNotFound, LocalCharmRepository, path)
75 self.assertEquals(str(err), "No repository found at %r" % path)94 self.assertEquals(str(err), "No repository found at %r" % path)
7695
77 @inlineCallbacks
78 def assert_not_there(self, name, repo, revision=None):
79 url = self.charm_url(name)
80 msg = "Charm 'local:series/%s' not found in repository %s" % (
81 name, repo.path)
82 err = yield self.assertFailure(repo.find(url), CharmNotFound)
83 self.assertEquals(str(err), msg)
84 if revision is None:
85 err = yield self.assertFailure(repo.latest(url), CharmNotFound)
86 self.assertEquals(str(err), msg)
87
88 def test_find_inappropriate_url(self):96 def test_find_inappropriate_url(self):
89 url = CharmURL.parse("cs:foo/bar")97 url = CharmURL.parse("cs:foo/bar")
90 err = self.assertRaises(AssertionError, self.repository1.find, url)98 err = self.assertRaises(AssertionError, self.repository1.find, url)
@@ -93,12 +101,31 @@
93 def test_completely_missing(self):101 def test_completely_missing(self):
94 return self.assert_not_there("zebra", self.repository1)102 return self.assert_not_there("zebra", self.repository1)
95103
96 def test_unkown_files_ignored(self):104 def test_unknown_files_ignored(self):
97 self.makeFile(105 self.makeFile(
98 "Foobar",106 "Foobar",
99 path=os.path.join(self.repository1.path, "series", "zebra"))107 path=os.path.join(self.repository1.path, "series", "zebra"))
100 return self.assert_not_there("zebra", self.repository1)108 return self.assert_not_there("zebra", self.repository1)
101109
110 @inlineCallbacks
111 def test_random_error_logged(self):
112 get_charm = self.mocker.replace(provider.get_charm_from_path)
113 get_charm(ANY)
114 self.mocker.throw(SyntaxError("magic"))
115 self.mocker.count(0, 3)
116 self.mocker.replay()
117
118 yield self.assertFailure(
119 self.repository1.find(self.charm_url("zebra")),
120 CharmNotFound)
121
122 self.assertIn(
123 "Unexpected error while processing",
124 self.output.getvalue())
125 self.assertIn(
126 "SyntaxError('magic',)",
127 self.output.getvalue())
128
102 def test_unknown_directories_ignored(self):129 def test_unknown_directories_ignored(self):
103 self.makeDir(130 self.makeDir(
104 path=os.path.join(self.repository1.path, "series", "zebra"))131 path=os.path.join(self.repository1.path, "series", "zebra"))
@@ -116,10 +143,9 @@
116revision: 0143revision: 0
117summary: hola""")144summary: hola""")
118 fh.close()145 fh.close()
119 stream = self.capture_logging("juju.charm")
120 yield self.assertFailure(146 yield self.assertFailure(
121 self.repository1.find(self.charm_url("zebra")), CharmNotFound)147 self.repository1.find(self.charm_url("zebra")), CharmNotFound)
122 output = stream.getvalue()148 output = self.output.getvalue()
123 self.assertIn(149 self.assertIn(
124 "Charm 'zebra' has a YAML error", output)150 "Charm 'zebra' has a YAML error", output)
125 self.assertIn(151 self.assertIn(
@@ -140,9 +166,8 @@
140revision: 0166revision: 0
141summary: hola""")167summary: hola""")
142 fh.close()168 fh.close()
143 stream = self.capture_logging("juju.charm")
144 yield self.repository1.find(self.charm_url("sample"))169 yield self.repository1.find(self.charm_url("sample"))
145 output = stream.getvalue()170 output = self.output.getvalue()
146 self.assertIn(171 self.assertIn(
147 "Charm 'mysql' has a YAML error", output)172 "Charm 'mysql' has a YAML error", output)
148 self.assertIn(173 self.assertIn(
@@ -169,13 +194,6 @@
169 def test_repo_type(self):194 def test_repo_type(self):
170 self.assertEqual(self.repository1.type, "local")195 self.assertEqual(self.repository1.type, "local")
171196
172 def assert_there(self, name, repo, revision, latest_revision=None):
173 url = self.charm_url(name)
174 charm = yield repo.find(url)
175 self.assertEquals(charm.get_revision(), revision)
176 latest = yield repo.latest(url)
177 self.assertEquals(latest, latest_revision or revision)
178
179 @inlineCallbacks197 @inlineCallbacks
180 def test_success_unbundled(self):198 def test_success_unbundled(self):
181 yield self.assert_there("sample", self.repository1, 2)199 yield self.assert_there("sample", self.repository1, 2)

Subscribers

People subscribed via source and target branches

to status/vote changes: