Merge lp:~fwereade/juju-core/errors-cleanup into lp:~juju/juju-core/trunk

Proposed by William Reade
Status: Rejected
Rejected by: William Reade
Proposed branch: lp:~fwereade/juju-core/errors-cleanup
Merge into: lp:~juju/juju-core/trunk
Diff against target: 821 lines (+214/-133)
22 files modified
charm/repo.go (+8/-15)
charm/repo_test.go (+25/-16)
cmd/juju/deploy_test.go (+1/-1)
cmd/juju/publish.go (+8/-6)
cmd/juju/upgradecharm_test.go (+7/-6)
cmd/jujud/agent_test.go (+11/-10)
cmd/jujud/bootstrap_test.go (+6/-6)
environs/dummy/storage.go (+1/-1)
environs/ec2/storage.go (+1/-1)
environs/imagemetadata/simplestreams.go (+5/-5)
environs/local/storage.go (+1/-1)
environs/maas/storage.go (+2/-2)
environs/openstack/storage.go (+1/-1)
environs/tools.go (+1/-1)
environs/tools_test.go (+4/-4)
errors/errors.go (+42/-40)
errors/errors_test.go (+74/-0)
state/apiserver/client_test.go (+1/-1)
state/apiserver/errors_test.go (+2/-2)
state/open.go (+8/-4)
state/state_test.go (+0/-7)
testing/charm.go (+5/-3)
To merge this branch: bzr merge lp:~fwereade/juju-core/errors-cleanup
Reviewer Review Type Date Requested Status
Tim Penhey (community) Approve
Review via email: mp+168928@code.launchpad.net

Description of the change

errors: tests, consistency

We weren't testing or making sensible use of the embedded errors in
NotFoundError and UnauthorizedError; we were doing inappropriate things
with both error types in various situations; the action of NotFoundf was
not consistent with Unauthorizedf.

These conditions no longer apply; and the various charm.Repository types in
play now produce errors.NotFoundError instead of charm.NotFoundError.

I'm also more than somewhat freaked out by the "auth fails" magic string.
Can anyone explain that?

https://codereview.appspot.com/9830049/

To post a comment you must log in.
Revision history for this message
William Reade (fwereade) wrote :

Reviewers: mp+168928_code.launchpad.net,

Message:
Please take a look.

Description:
errors: tests, consistency

We weren't testing or making sensible use of the embedded errors in
NotFoundError and UnauthorizedError; we were doing inappropriate things
with both error types in various situations; the action of NotFoundf was
not consistent with Unauthorizedf.

These conditions no longer apply; and the various charm.Repository types
in
play now produce errors.NotFoundError instead of charm.NotFoundError.

I'm also more than somewhat freaked out by the "auth fails" magic
string.
Can anyone explain that?

https://code.launchpad.net/~fwereade/juju-core/errors-cleanup/+merge/168928

(do not edit description out of merge proposal)

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

Affected files:
   A [revision details]
   M charm/repo.go
   M charm/repo_test.go
   M cmd/juju/deploy_test.go
   M cmd/juju/publish.go
   M cmd/juju/upgradecharm_test.go
   M cmd/jujud/agent_test.go
   M cmd/jujud/bootstrap_test.go
   M environs/dummy/storage.go
   M environs/ec2/storage.go
   M environs/imagemetadata/simplestreams.go
   M environs/local/storage.go
   M environs/maas/storage.go
   M environs/openstack/storage.go
   M environs/tools.go
   M environs/tools_test.go
   M errors/errors.go
   A errors/errors_test.go
   M state/apiserver/client_test.go
   M state/apiserver/errors_test.go
   M state/open.go
   M state/state_test.go
   M testing/charm.go

Revision history for this message
Roger Peppe (rogpeppe) wrote :

LGTM with some suggestions. i'd like to lose the stuttering and this
seems like a reasonable CL to do it in.

https://codereview.appspot.com/9830049/diff/1/charm/repo.go
File charm/repo.go (right):

https://codereview.appspot.com/9830049/diff/1/charm/repo.go#newcode288
charm/repo.go:288: return errors.RawNotFoundf("charm %q not found in
repository %q", curl, repoPath)
NotFoundf("charm %q in repository %q", curl, repoPath) ?

https://codereview.appspot.com/9830049/diff/1/environs/openstack/storage.go
File environs/openstack/storage.go (right):

https://codereview.appspot.com/9830049/diff/1/environs/openstack/storage.go#newcode11
environs/openstack/storage.go:11: coreerrors
"launchpad.net/juju-core/errors"
looks like this can lose the import alias.

https://codereview.appspot.com/9830049/diff/1/environs/tools_test.go
File environs/tools_test.go (right):

https://codereview.appspot.com/9830049/diff/1/environs/tools_test.go#newcode171
environs/tools_test.go:171: c.Check(err, DeepEquals,
&errors.NotFoundError{test.err.Error()})
in other tests we do check IsNotFound and the error message rather than
using DeepEquals. would that be better here too?
alternatively we could change those other tests to use this idiom,
which seems fine too.

https://codereview.appspot.com/9830049/diff/1/errors/errors.go
File errors/errors.go (right):

https://codereview.appspot.com/9830049/diff/1/errors/errors.go#newcode11
errors/errors.go:11: type NotFoundError struct {
s/NotFoundError/NotFound/ ?
(as discussed)

and ditto for Unauthorized and the Is* functions.

https://codereview.appspot.com/9830049/diff/1/state/apiserver/errors_test.go
File state/apiserver/errors_test.go (right):

https://codereview.appspot.com/9830049/diff/1/state/apiserver/errors_test.go#newcode26
state/apiserver/errors_test.go:26: err: errors.RawNotFoundf("hello"),
no real need to use Raw here, is there?

https://codereview.appspot.com/9830049/diff/1/state/open.go
File state/open.go (right):

https://codereview.appspot.com/9830049/diff/1/state/open.go#newcode188
state/open.go:188: // error value produced my mongo, or mgo, or
something? And what about
s/my mongo/by mongo/

yes, i seem to remember that in some cases, mongo
does not produce an error code when access is unauthorised.
i believe gustavo filed an issue for that, but
i don't recall where or when.

about the "need to login" error, i don't *think*
we need to check that here, as it should happen only
when setting passwords AFAICS (that error is also checked in
SetAdminMongoPassword BTW), but it's also a similar issue
re: lack of mongo error code.

https://codereview.appspot.com/9830049/diff/1/testing/charm.go
File testing/charm.go (right):

https://codereview.appspot.com/9830049/diff/1/testing/charm.go#newcode174
testing/charm.go:174: return nil, errors.RawNotFoundf("charm %q not
found in mock store", charmURL)
NotFoundf("charm %q in mock store", charmURL)
(and below)?

https://codereview.appspot.com/9830049/

Revision history for this message
Tim Penhey (thumper) wrote :

https://codereview.appspot.com/9830049/diff/1/charm/repo.go
File charm/repo.go (right):

https://codereview.appspot.com/9830049/diff/1/charm/repo.go#newcode288
charm/repo.go:288: return errors.RawNotFoundf("charm %q not found in
repository %q", curl, repoPath)
On 2013/06/12 13:51:05, rog wrote:
> NotFoundf("charm %q in repository %q", curl, repoPath) ?

What is the difference between NotFoundf and RawNotFoundf?

https://codereview.appspot.com/9830049/diff/1/charm/repo_test.go
File charm/repo_test.go (right):

https://codereview.appspot.com/9830049/diff/1/charm/repo_test.go#newcode188
charm/repo_test.go:188: c.Assert(errors.IsNotFoundError(err), Equals,
true)
Nice. Although how about a matcher?

   c.Assert(err, IsNotFoundError)
or
   c.Assert(err, errors.IsNotFound)

https://codereview.appspot.com/9830049/diff/1/environs/imagemetadata/simplestreams.go
File environs/imagemetadata/simplestreams.go (right):

https://codereview.appspot.com/9830049/diff/1/environs/imagemetadata/simplestreams.go#newcode248
environs/imagemetadata/simplestreams.go:248: return nil, dataURL,
errors.RawNotFoundf("invalid URL %q", dataURL)
Is there a good reason why we have two functions? RawNotFoundf and
NotFoundf? Why have two same but different functions/types?

https://codereview.appspot.com/9830049/diff/1/errors/errors.go
File errors/errors.go (right):

https://codereview.appspot.com/9830049/diff/1/errors/errors.go#newcode11
errors/errors.go:11: type NotFoundError struct {
On 2013/06/12 13:51:05, rog wrote:
> s/NotFoundError/NotFound/ ?
> (as discussed)

> and ditto for Unauthorized and the Is* functions.

+1 as almost always namespaced in the caller code with the package.
   errors.IsNotFound
seems nicer to me than
   errors.IsNotFoundError

Hopefully the package name makes it obvious.

(Message brought to you by the department of redundancy department)

https://codereview.appspot.com/9830049/diff/1/errors/errors.go#newcode35
errors/errors.go:35: // "foo bar".
The name of the method and be behaviour seem to be at odds to me.

https://codereview.appspot.com/9830049/

Revision history for this message
Tim Penhey (thumper) wrote :

On 2013/06/18 23:33:17, thumper wrote:
> https://codereview.appspot.com/9830049/diff/1/charm/repo.go
> File charm/repo.go (right):

> https://codereview.appspot.com/9830049/diff/1/charm/repo.go#newcode288
> charm/repo.go:288: return errors.RawNotFoundf("charm %q not found in
repository
> %q", curl, repoPath)
> On 2013/06/12 13:51:05, rog wrote:
> > NotFoundf("charm %q in repository %q", curl, repoPath) ?

> What is the difference between NotFoundf and RawNotFoundf?

> https://codereview.appspot.com/9830049/diff/1/charm/repo_test.go
> File charm/repo_test.go (right):

https://codereview.appspot.com/9830049/diff/1/charm/repo_test.go#newcode188
> charm/repo_test.go:188: c.Assert(errors.IsNotFoundError(err), Equals,
true)
> Nice. Although how about a matcher?

> c.Assert(err, IsNotFoundError)
> or
> c.Assert(err, errors.IsNotFound)

https://codereview.appspot.com/9830049/diff/1/environs/imagemetadata/simplestreams.go
> File environs/imagemetadata/simplestreams.go (right):

https://codereview.appspot.com/9830049/diff/1/environs/imagemetadata/simplestreams.go#newcode248
> environs/imagemetadata/simplestreams.go:248: return nil, dataURL,
> errors.RawNotFoundf("invalid URL %q", dataURL)
> Is there a good reason why we have two functions? RawNotFoundf and
NotFoundf?
> Why have two same but different functions/types?

> https://codereview.appspot.com/9830049/diff/1/errors/errors.go
> File errors/errors.go (right):

https://codereview.appspot.com/9830049/diff/1/errors/errors.go#newcode11
> errors/errors.go:11: type NotFoundError struct {
> On 2013/06/12 13:51:05, rog wrote:
> > s/NotFoundError/NotFound/ ?
> > (as discussed)
> >
> > and ditto for Unauthorized and the Is* functions.

> +1 as almost always namespaced in the caller code with the package.
> errors.IsNotFound
> seems nicer to me than
> errors.IsNotFoundError

> Hopefully the package name makes it obvious.

> (Message brought to you by the department of redundancy department)

https://codereview.appspot.com/9830049/diff/1/errors/errors.go#newcode35
> errors/errors.go:35: // "foo bar".
> The name of the method and be behaviour seem to be at odds to me.

LGTM - it is better than what was there before, lets not depend on
perfection

https://codereview.appspot.com/9830049/

Revision history for this message
Tim Penhey (thumper) :
review: Approve
Revision history for this message
John A Meinel (jameinel) wrote :

Note this is still targeted at the old trunk.

Unmerged revisions

1268. By William Reade

mock charm store now uses errors.NotFoundError

1267. By William Reade

merge parent

1266. By William Reade

make errors somewhat consistent

1265. By William Reade

merge parent

1264. By William Reade

trivial error message fix (but notice how little it's tested for...)

1263. By William Reade

rewrite charm.Config for clarity, adding currently-unused ParseSettingsYAML method and new Settings type

1262. By Ian Booth

Move assignment policy to global env config

Previoysly, each provider defined it's own AssignmentPolicy,
which was hard coded to AssignNew. Now, it's been made an
env setting with default AssignNew. With containers, it will be
possible to add a unit to an existing machine (if not dirty) so
this work is a step in that direction.

R=jameinel, fwereade
CC=
https://codereview.appspot.com/9824043

1261. By Ian Booth

Add dirty flag to machine state

When choosing an unused machine to assign a unit to, a check was being made
to see if there were any principal units for the machine. If machines had units
assigned and then unassigned, the machine would appear clean since number of
principal unit goes to 0 but the machine should still be considered dirty.

The machine state has a "clean" flag (boolean). Machines are created with the
clean flag set to true. It is set false when a machine has a unit assigned,
and not reset when units are unassigned. The assign to unused machine logic
is updated to look at the clean flag.

R=fwereade, thumper
CC=
https://codereview.appspot.com/9820043

1260. By Tim Penhey

First part of the provisioner refactoring.

This branch breaks up the current provisioner and defines some interfaces that
we'll be using for containers.

The broker interface is what we have starting, stopping instances, and listing
instances and related machines.

An environment broker is written that defers the actual calls to the environ.

The provisioner now creates a provisioning task for the environment provider
using a machine watcher and the environment broker. The common provisioning
methods are now in provisioner_task.go.

I refactored some of the methods as we were duplicating a lot of calls, like
get all instances, and then getting instances for individual ids. Also,
looking up specific machines, and also getting all machines. Now we just do
the "all" gets, and keep a map around for the duration of the checks.

R=wallyworld, mue, fwereade, rog
CC=
https://codereview.appspot.com/9937046

1259. By Frank Mueller

state: added CleanupWatcher

The CleanupWatcher signals the demand for the running of
state.Cleanup(). It is the first in a row of CLs to add
an according worker.

R=fwereade, dimitern
CC=
https://codereview.appspot.com/10078043

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charm/repo.go'
2--- charm/repo.go 2013-06-10 20:50:42 +0000
3+++ charm/repo.go 2013-06-12 12:32:25 +0000
4@@ -10,12 +10,14 @@
5 "fmt"
6 "io"
7 "io/ioutil"
8- "launchpad.net/juju-core/log"
9 "net/http"
10 "net/url"
11 "os"
12 "path/filepath"
13 "strings"
14+
15+ "launchpad.net/juju-core/errors"
16+ "launchpad.net/juju-core/log"
17 )
18
19 // CacheDir stores the charm cache directory path.
20@@ -46,15 +48,6 @@
21 Latest(curl *URL) (int, error)
22 }
23
24-// NotFoundError represents an error indicating that the requested data wasn't found.
25-type NotFoundError struct {
26- msg string
27-}
28-
29-func (e *NotFoundError) Error() string {
30- return e.msg
31-}
32-
33 // CharmStore is a Repository that provides access to the public juju charm store.
34 type CharmStore struct {
35 BaseURL string
36@@ -83,7 +76,7 @@
37 return nil, fmt.Errorf("charm: charm store returned response without charm %q", key)
38 }
39 if len(info.Errors) == 1 && info.Errors[0] == "entry not found" {
40- return nil, &NotFoundError{fmt.Sprintf("charm not found: %s", curl)}
41+ return nil, errors.NotFoundf("charm %q", curl)
42 }
43 return info, nil
44 }
45@@ -116,9 +109,9 @@
46 }
47 if len(event.Errors) == 1 && event.Errors[0] == "entry not found" {
48 if digest == "" {
49- return nil, &NotFoundError{fmt.Sprintf("charm event not found for %q", curl)}
50+ return nil, errors.NotFoundf("charm event for %q", curl)
51 } else {
52- return nil, &NotFoundError{fmt.Sprintf("charm event not found for %q with digest %q", curl, digest)}
53+ return nil, errors.NotFoundf("charm event for %q with digest %q", curl, digest)
54 }
55 }
56 return event, nil
57@@ -288,11 +281,11 @@
58 }
59
60 func repoNotFound(path string) error {
61- return &NotFoundError{fmt.Sprintf("no repository found at %q", path)}
62+ return errors.NotFoundf("repository %q", path)
63 }
64
65 func charmNotFound(curl *URL, repoPath string) error {
66- return &NotFoundError{fmt.Sprintf("charm not found in %q: %s", repoPath, curl)}
67+ return errors.RawNotFoundf("charm %q not found in repository %q", curl, repoPath)
68 }
69
70 func mightBeCharm(info os.FileInfo) bool {
71
72=== modified file 'charm/repo_test.go'
73--- charm/repo_test.go 2013-06-11 23:32:05 +0000
74+++ charm/repo_test.go 2013-06-12 12:32:25 +0000
75@@ -9,15 +9,17 @@
76 "encoding/json"
77 "fmt"
78 "io/ioutil"
79- . "launchpad.net/gocheck"
80- "launchpad.net/juju-core/charm"
81- "launchpad.net/juju-core/testing"
82 "net"
83 "net/http"
84 "os"
85 "path/filepath"
86 "strconv"
87 "strings"
88+
89+ . "launchpad.net/gocheck"
90+ "launchpad.net/juju-core/charm"
91+ "launchpad.net/juju-core/errors"
92+ "launchpad.net/juju-core/testing"
93 )
94
95 type MockStore struct {
96@@ -180,11 +182,13 @@
97
98 func (s *StoreSuite) TestMissing(c *C) {
99 charmURL := charm.MustParseURL("cs:series/missing")
100- expect := `charm not found: cs:series/missing`
101+ expect := `charm "cs:series/missing" not found`
102 _, err := s.store.Latest(charmURL)
103 c.Assert(err, ErrorMatches, expect)
104+ c.Assert(errors.IsNotFoundError(err), Equals, true)
105 _, err = s.store.Get(charmURL)
106 c.Assert(err, ErrorMatches, expect)
107+ c.Assert(errors.IsNotFoundError(err), Equals, true)
108 }
109
110 func (s *StoreSuite) TestError(c *C) {
111@@ -280,7 +284,8 @@
112 func (s *StoreSuite) TestInfoNotFound(c *C) {
113 charmURL := charm.MustParseURL("cs:series/missing")
114 info, err := s.store.Info(charmURL)
115- c.Assert(err, ErrorMatches, `charm not found: cs:series/missing`)
116+ c.Assert(err, ErrorMatches, `charm "cs:series/missing" not found`)
117+ c.Assert(errors.IsNotFoundError(err), Equals, true)
118 c.Assert(info, IsNil)
119 }
120
121@@ -319,14 +324,16 @@
122 func (s *StoreSuite) TestEventNotFound(c *C) {
123 charmURL := charm.MustParseURL("cs:series/missing")
124 event, err := s.store.Event(charmURL, "")
125- c.Assert(err, ErrorMatches, `charm event not found for "cs:series/missing"`)
126+ c.Assert(err, ErrorMatches, `charm event for "cs:series/missing" not found`)
127+ c.Assert(errors.IsNotFoundError(err), Equals, true)
128 c.Assert(event, IsNil)
129 }
130
131 func (s *StoreSuite) TestEventNotFoundDigest(c *C) {
132 charmURL := charm.MustParseURL("cs:series/good")
133 event, err := s.store.Event(charmURL, "missing-digest")
134- c.Assert(err, ErrorMatches, `charm event not found for "cs:series/good" with digest "missing-digest"`)
135+ c.Assert(err, ErrorMatches, `charm event for "cs:series/good" with digest "missing-digest" not found`)
136+ c.Assert(errors.IsNotFoundError(err), Equals, true)
137 c.Assert(event, IsNil)
138 }
139
140@@ -414,7 +421,7 @@
141 }
142
143 func (s *LocalRepoSuite) checkNotFoundErr(c *C, err error, charmURL *charm.URL) {
144- expect := `charm not found in "` + s.repo.Path + `": ` + charmURL.String()
145+ expect := fmt.Sprintf("charm %q not found in repository %q", charmURL, s.repo.Path)
146 c.Check(err, ErrorMatches, expect)
147 }
148
149@@ -432,16 +439,18 @@
150 }
151
152 func (s *LocalRepoSuite) TestMissingRepo(c *C) {
153+ curl := charm.MustParseURL("local:series/zebra")
154+ expect := fmt.Sprintf("repository %q not found", s.repo.Path)
155+ checkNotFound := func() {
156+ _, err := s.repo.Latest(curl)
157+ c.Check(err, ErrorMatches, expect)
158+ _, err = s.repo.Get(curl)
159+ c.Check(err, ErrorMatches, expect)
160+ }
161 c.Assert(os.RemoveAll(s.repo.Path), IsNil)
162- _, err := s.repo.Latest(charm.MustParseURL("local:series/zebra"))
163- c.Assert(err, ErrorMatches, `no repository found at ".*"`)
164- _, err = s.repo.Get(charm.MustParseURL("local:series/zebra"))
165- c.Assert(err, ErrorMatches, `no repository found at ".*"`)
166+ checkNotFound()
167 c.Assert(ioutil.WriteFile(s.repo.Path, nil, 0666), IsNil)
168- _, err = s.repo.Latest(charm.MustParseURL("local:series/zebra"))
169- c.Assert(err, ErrorMatches, `no repository found at ".*"`)
170- _, err = s.repo.Get(charm.MustParseURL("local:series/zebra"))
171- c.Assert(err, ErrorMatches, `no repository found at ".*"`)
172+ checkNotFound()
173 }
174
175 func (s *LocalRepoSuite) TestMultipleVersions(c *C) {
176
177=== modified file 'cmd/juju/deploy_test.go'
178--- cmd/juju/deploy_test.go 2013-06-12 11:25:32 +0000
179+++ cmd/juju/deploy_test.go 2013-06-12 12:32:25 +0000
180@@ -62,7 +62,7 @@
181
182 func (s *DeploySuite) TestNoCharm(c *C) {
183 err := runDeploy(c, "local:unknown-123")
184- c.Assert(err, ErrorMatches, `cannot get charm: charm not found in ".*": local:precise/unknown-123`)
185+ c.Assert(err, ErrorMatches, `cannot get charm: charm "local:precise/unknown-123" not found in repository ".*"`)
186 }
187
188 func (s *DeploySuite) TestCharmDir(c *C) {
189
190=== modified file 'cmd/juju/publish.go'
191--- cmd/juju/publish.go 2013-05-02 15:55:42 +0000
192+++ cmd/juju/publish.go 2013-06-12 12:32:25 +0000
193@@ -5,14 +5,16 @@
194
195 import (
196 "fmt"
197+ "os"
198+ "strings"
199+ "time"
200+
201 "launchpad.net/gnuflag"
202 "launchpad.net/juju-core/bzr"
203 "launchpad.net/juju-core/charm"
204 "launchpad.net/juju-core/cmd"
205+ "launchpad.net/juju-core/errors"
206 "launchpad.net/juju-core/log"
207- "os"
208- "strings"
209- "time"
210 )
211
212 type PublishCommand struct {
213@@ -131,9 +133,9 @@
214 }
215
216 oldEvent, err := charm.Store.Event(curl, localDigest)
217- if _, ok := err.(*charm.NotFoundError); ok {
218+ if errors.IsNotFoundError(err) {
219 oldEvent, err = charm.Store.Event(curl, "")
220- if _, ok := err.(*charm.NotFoundError); ok {
221+ if errors.IsNotFoundError(err) {
222 log.Infof("charm %s is not yet in the store", curl)
223 err = nil
224 }
225@@ -156,7 +158,7 @@
226 for {
227 time.Sleep(c.pollDelay)
228 newEvent, err := charm.Store.Event(curl, "")
229- if _, ok := err.(*charm.NotFoundError); ok {
230+ if errors.IsNotFoundError(err) {
231 continue
232 }
233 if err != nil {
234
235=== modified file 'cmd/juju/upgradecharm_test.go'
236--- cmd/juju/upgradecharm_test.go 2013-06-10 20:50:42 +0000
237+++ cmd/juju/upgradecharm_test.go 2013-06-12 12:32:25 +0000
238@@ -41,12 +41,12 @@
239 c.Assert(err, IsNil)
240
241 err = runUpgradeCharm(c, "riak", "--repository=blah")
242- c.Assert(err, ErrorMatches, `no repository found at ".*blah"`)
243+ c.Assert(err, ErrorMatches, `repository ".*blah" not found`)
244 // Reset JUJU_REPOSITORY explicitly, because repoSuite.SetUpTest
245 // overwrites it (TearDownTest will revert it again).
246 os.Setenv("JUJU_REPOSITORY", "")
247 err = runUpgradeCharm(c, "riak", "--repository=")
248- c.Assert(err, ErrorMatches, `charm not found in ".*": local:precise/riak`)
249+ c.Assert(err, ErrorMatches, `charm "local:precise/riak" not found in repository ".*"`)
250 }
251
252 func (s *UpgradeCharmErrorsSuite) TestInvalidService(c *C) {
253@@ -70,11 +70,12 @@
254
255 func (s *UpgradeCharmErrorsSuite) TestInvalidSwitchURL(c *C) {
256 s.deployService(c)
257- err := runUpgradeCharm(c, "riak", "--switch=blah")
258- c.Assert(err, ErrorMatches, "charm not found: cs:precise/blah")
259- err = runUpgradeCharm(c, "riak", "--switch=cs:missing/one")
260- c.Assert(err, ErrorMatches, "charm not found: cs:missing/one")
261+ err := runUpgradeCharm(c, "riak", "--switch=local:blah")
262+ c.Assert(err, ErrorMatches, `charm "local:precise/blah" not found in repository ".*"`)
263+ err = runUpgradeCharm(c, "riak", "--switch=local:missing/one")
264+ c.Assert(err, ErrorMatches, `charm "local:missing/one" not found in repository ".*"`)
265 // TODO(dimitern): add tests with incompatible charms
266+ // TODO(fwereade): hook up forthcoming testing.MockCharmStore for cs: tests
267 }
268
269 func (s *UpgradeCharmErrorsSuite) TestSwitchAndRevisionFails(c *C) {
270
271=== modified file 'cmd/jujud/agent_test.go'
272--- cmd/jujud/agent_test.go 2013-05-31 08:33:34 +0000
273+++ cmd/jujud/agent_test.go 2013-06-12 12:32:25 +0000
274@@ -6,21 +6,22 @@
275 import (
276 "bytes"
277 "fmt"
278+ "net/http"
279+ "os"
280+ "path/filepath"
281+ "time"
282+
283 . "launchpad.net/gocheck"
284 "launchpad.net/juju-core/cmd"
285 "launchpad.net/juju-core/environs/agent"
286 "launchpad.net/juju-core/environs/tools"
287- "launchpad.net/juju-core/errors"
288- "launchpad.net/juju-core/juju/testing"
289 "launchpad.net/juju-core/state"
290- coretesting "launchpad.net/juju-core/testing"
291 "launchpad.net/juju-core/version"
292 "launchpad.net/juju-core/worker"
293 "launchpad.net/tomb"
294- "net/http"
295- "os"
296- "path/filepath"
297- "time"
298+
299+ "launchpad.net/juju-core/juju/testing"
300+ coretesting "launchpad.net/juju-core/testing"
301 )
302
303 var _ = Suite(&toolSuite{})
304@@ -348,13 +349,13 @@
305 info := s.StateInfo(c)
306 info.Tag = ent.Tag()
307 info.Password = "initial"
308- testOpenState(c, info, errors.Unauthorizedf("unauth"))
309+ assertOpenState(c, info, false)
310
311 // Read the configuration and check that we can connect with it.
312 c.Assert(refreshConfig(conf), IsNil)
313 newPassword := conf.StateInfo.Password
314
315- testOpenState(c, conf.StateInfo, nil)
316+ assertOpenState(c, conf.StateInfo, true)
317
318 // Check that it starts again ok
319 err = runStop(newAgent())
320@@ -376,7 +377,7 @@
321 c.Assert(conf.StateInfo.Password, Not(Equals), newPassword)
322
323 info.Password = conf.StateInfo.Password
324- testOpenState(c, info, nil)
325+ assertOpenState(c, info, true)
326 }
327
328 func (s *agentSuite) testUpgrade(c *C, agent runner, currentTools *state.Tools) {
329
330=== modified file 'cmd/jujud/bootstrap_test.go'
331--- cmd/jujud/bootstrap_test.go 2013-05-30 00:47:30 +0000
332+++ cmd/jujud/bootstrap_test.go 2013-06-12 12:32:25 +0000
333@@ -134,15 +134,15 @@
334 })
335 }
336
337-func testOpenState(c *C, info *state.Info, expectErr error) {
338+func assertOpenState(c *C, info *state.Info, expectSuccess bool) {
339 st, err := state.Open(info, state.DefaultDialOpts())
340 if st != nil {
341 st.Close()
342 }
343- if expectErr != nil {
344+ if expectSuccess {
345+ c.Assert(err, IsNil)
346+ } else {
347 c.Assert(errors.IsUnauthorizedError(err), Equals, true)
348- } else {
349- c.Assert(err, IsNil)
350 }
351 }
352
353@@ -162,10 +162,10 @@
354 Addrs: []string{testing.MgoAddr},
355 CACert: []byte(testing.CACert),
356 }
357- testOpenState(c, info, errors.Unauthorizedf("some auth problem"))
358+ assertOpenState(c, info, false)
359
360 info.Tag, info.Password = "machine-0", "foo"
361- testOpenState(c, info, nil)
362+ assertOpenState(c, info, true)
363
364 info.Tag = ""
365 st, err := state.Open(info, state.DefaultDialOpts())
366
367=== modified file 'environs/dummy/storage.go'
368--- environs/dummy/storage.go 2013-05-30 00:47:30 +0000
369+++ environs/dummy/storage.go 2013-06-12 12:32:25 +0000
370@@ -82,7 +82,7 @@
371 }
372 data, ok := s.files[path]
373 if !ok {
374- return nil, errors.NotFoundf("file %q not found", path)
375+ return nil, errors.NotFoundf("file %q", path)
376 }
377 return data, nil
378 }
379
380=== modified file 'environs/ec2/storage.go'
381--- environs/ec2/storage.go 2013-05-30 00:47:30 +0000
382+++ environs/ec2/storage.go 2013-06-12 12:32:25 +0000
383@@ -160,7 +160,7 @@
384
385 func maybeNotFound(err error) error {
386 if err != nil && s3ErrorStatusCode(err) == 404 {
387- return &errors.NotFoundError{err, ""}
388+ return errors.RawNotFoundf("%v", err)
389 }
390 return err
391 }
392
393=== modified file 'environs/imagemetadata/simplestreams.go'
394--- environs/imagemetadata/simplestreams.go 2013-05-30 05:39:33 +0000
395+++ environs/imagemetadata/simplestreams.go 2013-06-12 12:32:25 +0000
396@@ -245,14 +245,14 @@
397 dataURL += path
398 resp, err := http.Get(dataURL)
399 if err != nil {
400- return nil, dataURL, errors.NotFoundf("invalid URL %q", dataURL)
401+ return nil, dataURL, errors.RawNotFoundf("invalid URL %q", dataURL)
402 }
403 defer resp.Body.Close()
404 if resp.StatusCode == http.StatusNotFound {
405- return nil, dataURL, errors.NotFoundf("cannot find URL %q", dataURL)
406+ return nil, dataURL, errors.NotFoundf("URL %q", dataURL)
407 }
408 if resp.StatusCode == http.StatusUnauthorized {
409- return nil, dataURL, errors.Unauthorizedf("unauthorised access to URL %q", dataURL)
410+ return nil, dataURL, errors.Unauthorizedf("access to URL %q", dataURL)
411 }
412 if resp.StatusCode != http.StatusOK {
413 return nil, dataURL, fmt.Errorf("cannot access URL %q, %q", dataURL, resp.Status)
414@@ -322,9 +322,9 @@
415 }
416 }
417 if !containsImageIds {
418- return "", errors.NotFoundf("index file missing %q data", imageIds)
419+ return "", errors.RawNotFoundf("index file missing %q data", imageIds)
420 }
421- return "", errors.NotFoundf("index file missing data for cloud %v and product name(s) %q", ic.CloudSpec, prodIds)
422+ return "", errors.RawNotFoundf("index file missing data for cloud %v and product name(s) %q", ic.CloudSpec, prodIds)
423 }
424
425 // utility function to see if element exists in values slice.
426
427=== modified file 'environs/local/storage.go'
428--- environs/local/storage.go 2013-05-30 00:47:30 +0000
429+++ environs/local/storage.go 2013-06-12 12:32:25 +0000
430@@ -42,7 +42,7 @@
431 return nil, err
432 }
433 if resp.StatusCode != 200 {
434- return nil, errors.NotFoundf("file %q not found", name)
435+ return nil, errors.NotFoundf("file %q", name)
436 }
437 return resp.Body, nil
438 }
439
440=== modified file 'environs/maas/storage.go'
441--- environs/maas/storage.go 2013-05-30 00:47:30 +0000
442+++ environs/maas/storage.go 2013-06-12 12:32:25 +0000
443@@ -77,9 +77,9 @@
444 noObj := gomaasapi.MAASObject{}
445 serverErr, ok := err.(gomaasapi.ServerError)
446 if ok && serverErr.StatusCode == 404 {
447- return noObj, errors.NotFoundf("file '%s' not found", name)
448+ return noObj, errors.NotFoundf("file %q", name)
449 }
450- msg := fmt.Errorf("could not access file '%s': %v", name, err)
451+ msg := fmt.Errorf("could not access file %q: %v", name, err)
452 return noObj, msg
453 }
454 return obj, nil
455
456=== modified file 'environs/openstack/storage.go'
457--- environs/openstack/storage.go 2013-05-31 01:02:53 +0000
458+++ environs/openstack/storage.go 2013-06-12 12:32:25 +0000
459@@ -160,7 +160,7 @@
460 // container not being found.
461 func maybeNotFound(err error) (error, bool) {
462 if err != nil && gooseerrors.IsNotFound(err) {
463- return &coreerrors.NotFoundError{err, ""}, true
464+ return coreerrors.RawNotFoundf("%v", err), true
465 }
466 return err, false
467 }
468
469=== modified file 'environs/tools.go'
470--- environs/tools.go 2013-05-30 00:47:30 +0000
471+++ environs/tools.go 2013-06-12 12:32:25 +0000
472@@ -147,6 +147,6 @@
473
474 func convertToolsError(err *error) {
475 if isToolsError(*err) {
476- *err = &errors.NotFoundError{*err, ""}
477+ *err = errors.RawNotFoundf("%v", *err)
478 }
479 }
480
481=== modified file 'environs/tools_test.go'
482--- environs/tools_test.go 2013-05-30 00:47:30 +0000
483+++ environs/tools_test.go 2013-06-12 12:32:25 +0000
484@@ -168,7 +168,7 @@
485 if len(actual) > 0 {
486 c.Logf(actual.String())
487 }
488- c.Check(err, DeepEquals, &errors.NotFoundError{test.err, ""})
489+ c.Check(err, DeepEquals, &errors.NotFoundError{test.err.Error()})
490 continue
491 }
492 source := private
493@@ -389,7 +389,7 @@
494 if len(actual) > 0 {
495 c.Logf(actual.String())
496 }
497- c.Check(err, DeepEquals, &errors.NotFoundError{test.err, ""})
498+ c.Check(err, DeepEquals, &errors.NotFoundError{test.err.Error()})
499 continue
500 }
501 expect := map[version.Binary]string{}
502@@ -484,7 +484,7 @@
503 if len(actual) > 0 {
504 c.Logf(actual.String())
505 }
506- c.Check(err, DeepEquals, &errors.NotFoundError{test.err, ""})
507+ c.Check(err, DeepEquals, &errors.NotFoundError{test.err.Error()})
508 continue
509 }
510 expect := map[version.Binary]string{}
511@@ -548,7 +548,7 @@
512 }
513 c.Check(actual.URL, DeepEquals, source[actual.Binary])
514 } else {
515- c.Check(err, DeepEquals, &errors.NotFoundError{test.err, ""})
516+ c.Check(err, DeepEquals, &errors.NotFoundError{test.err.Error()})
517 }
518 }
519 }
520
521=== modified file 'errors/errors.go'
522--- errors/errors.go 2013-05-30 00:47:30 +0000
523+++ errors/errors.go 2013-06-12 12:32:25 +0000
524@@ -7,60 +7,62 @@
525 "fmt"
526 )
527
528-// NotFoundError records an error when something has not been found.
529+// NotFoundError indicates that something has not been found.
530 type NotFoundError struct {
531- // error is the underlying error.
532- error
533 Msg string
534 }
535
536-// IsNotFoundError returns true if err is a NotFoundError.
537+func (e *NotFoundError) Error() string {
538+ if e.Msg == "" {
539+ return "<something> not found"
540+ }
541+ return e.Msg
542+}
543+
544+// IsNotFoundError returns true if err is a *NotFoundError.
545 func IsNotFoundError(err error) bool {
546- if _, ok := err.(*NotFoundError); ok {
547- return true
548- }
549- return false
550-}
551-
552-func (e *NotFoundError) Error() string {
553- if e.Msg != "" || e.error == nil {
554- if e.error != nil {
555- return fmt.Sprintf("%s: %v", e.Msg, e.error.Error())
556- }
557- return e.Msg
558- }
559- return e.error.Error()
560-}
561-
562-// NotFoundf returns a error for which IsNotFound returns
563-// true. The message for the error is made up from the given
564-// arguments formatted as with fmt.Sprintf, with the
565-// string " not found" appended.
566+ _, ok := err.(*NotFoundError)
567+ return ok
568+}
569+
570+// NotFoundf("foo %s", "bar") returns a *NotFoundError with the message
571+// "foo bar not found".
572 func NotFoundf(format string, args ...interface{}) error {
573- return &NotFoundError{nil, fmt.Sprintf(format+" not found", args...)}
574-}
575-
576-// UnauthorizedError represents the error that an operation is unauthorized.
577-// Use IsUnauthorized() to determine if the error was related to authorization failure.
578+ return RawNotFoundf(format+" not found", args...)
579+}
580+
581+// RawNotFoundf("foo %s", "bar") returns a *NotFoundError with the message
582+// "foo bar".
583+func RawNotFoundf(format string, args ...interface{}) error {
584+ return &NotFoundError{fmt.Sprintf(format, args...)}
585+}
586+
587+// UnauthorizedError indicates that some operation is unauthorized.
588 type UnauthorizedError struct {
589- error
590 Msg string
591 }
592
593+func (e *UnauthorizedError) Error() string {
594+ if e.Msg == "" {
595+ return "unauthorized <something>"
596+ }
597+ return e.Msg
598+}
599+
600+// IsUnauthorizedError returns true if err is a *UnauthorizedError.
601 func IsUnauthorizedError(err error) bool {
602 _, ok := err.(*UnauthorizedError)
603 return ok
604 }
605
606-func (e *UnauthorizedError) Error() string {
607- if e.error != nil {
608- return fmt.Sprintf("%s: %v", e.Msg, e.error.Error())
609- }
610- return e.Msg
611-}
612-
613-// Unauthorizedf returns an error for which IsUnauthorizedError returns true.
614-// It is mainly used for testing.
615+// Unauthorizedf("foo %s", "bar") returns a *UnauthorizedError with the
616+// message "unauthorized foo bar".
617 func Unauthorizedf(format string, args ...interface{}) error {
618- return &UnauthorizedError{nil, fmt.Sprintf(format, args...)}
619+ return RawUnauthorizedf("unauthorized "+format, args...)
620+}
621+
622+// RawUnauthorizedf("foo %s", "bar") returns a *UnauthorizedError with the
623+// message "foo bar".
624+func RawUnauthorizedf(format string, args ...interface{}) error {
625+ return &UnauthorizedError{fmt.Sprintf(format, args...)}
626 }
627
628=== added file 'errors/errors_test.go'
629--- errors/errors_test.go 1970-01-01 00:00:00 +0000
630+++ errors/errors_test.go 2013-06-12 12:32:25 +0000
631@@ -0,0 +1,74 @@
632+// Copyright 2011, 2012, 2013 Canonical Ltd.
633+// Licensed under the AGPLv3, see LICENCE file for details.
634+
635+package errors_test
636+
637+import (
638+ "fmt"
639+ "testing"
640+
641+ . "launchpad.net/gocheck"
642+ "launchpad.net/juju-core/errors"
643+)
644+
645+func TestPackage(t *testing.T) {
646+ TestingT(t)
647+}
648+
649+type NotFoundSuite struct{}
650+
651+var _ = Suite(&NotFoundSuite{})
652+
653+func (s *NotFoundSuite) TestNotFound(c *C) {
654+ emptyErr := &errors.NotFoundError{}
655+ c.Check(emptyErr, ErrorMatches, `<something> not found`)
656+ c.Check(errors.IsNotFoundError(emptyErr), Equals, true)
657+
658+ rawErr := &errors.NotFoundError{"widget cannot be located"}
659+ c.Check(rawErr, ErrorMatches, `widget cannot be located`)
660+ c.Check(errors.IsNotFoundError(rawErr), Equals, true)
661+
662+ badErr := fmt.Errorf("thingy not found")
663+ c.Check(errors.IsNotFoundError(badErr), Equals, false)
664+
665+ c.Check(errors.IsNotFoundError(nil), Equals, false)
666+}
667+
668+func (s *NotFoundSuite) TestNotFoundf(c *C) {
669+ err := errors.NotFoundf("plop %q", "wibble")
670+ c.Check(err, ErrorMatches, `plop "wibble" not found`)
671+ c.Check(errors.IsNotFoundError(err), Equals, true)
672+
673+ err = errors.RawNotFoundf("plop %q", "wibble")
674+ c.Check(err, ErrorMatches, `plop "wibble"`)
675+ c.Check(errors.IsNotFoundError(err), Equals, true)
676+}
677+
678+type UnauthorizedSuite struct{}
679+
680+var _ = Suite(&UnauthorizedSuite{})
681+
682+func (s *UnauthorizedSuite) TestUnauthorized(c *C) {
683+ emptyErr := &errors.UnauthorizedError{}
684+ c.Check(emptyErr, ErrorMatches, `unauthorized <something>`)
685+ c.Check(errors.IsUnauthorizedError(emptyErr), Equals, true)
686+
687+ rawErr := &errors.UnauthorizedError{"whatchamadoodle access is forbidden"}
688+ c.Check(rawErr, ErrorMatches, `whatchamadoodle access is forbidden`)
689+ c.Check(errors.IsUnauthorizedError(rawErr), Equals, true)
690+
691+ badErr := fmt.Errorf("unauthorized thingy")
692+ c.Check(errors.IsUnauthorizedError(badErr), Equals, false)
693+
694+ c.Check(errors.IsUnauthorizedError(nil), Equals, false)
695+}
696+
697+func (s *UnauthorizedSuite) TestUnauthorizedf(c *C) {
698+ err := errors.Unauthorizedf("plop %q", "wibble")
699+ c.Check(err, ErrorMatches, `unauthorized plop "wibble"`)
700+ c.Check(errors.IsUnauthorizedError(err), Equals, true)
701+
702+ err = errors.RawUnauthorizedf("plop %q", "wibble")
703+ c.Check(err, ErrorMatches, `plop "wibble"`)
704+ c.Check(errors.IsUnauthorizedError(err), Equals, true)
705+}
706
707=== modified file 'state/apiserver/client_test.go'
708--- state/apiserver/client_test.go 2013-06-12 11:25:32 +0000
709+++ state/apiserver/client_test.go 2013-06-12 12:32:25 +0000
710@@ -309,7 +309,7 @@
711 "wordpress": `charm URL has invalid schema: "wordpress"`,
712 "cs:wordpress": `charm URL without series: "cs:wordpress"`,
713 "cs:precise/wordpress": "charm url must include revision",
714- "cs:precise/wordpress-999999": `cannot get charm: charm not found in mock store: cs:precise/wordpress-999999`,
715+ "cs:precise/wordpress-999999": `cannot get charm: charm "cs:precise/wordpress-999999" not found in mock store`,
716 "local:precise/wordpress-999999": `charm url has unsupported schema "local"`,
717 } {
718 c.Logf("test %s", url)
719
720=== modified file 'state/apiserver/errors_test.go'
721--- state/apiserver/errors_test.go 2013-06-06 17:09:49 +0000
722+++ state/apiserver/errors_test.go 2013-06-12 12:32:25 +0000
723@@ -23,10 +23,10 @@
724 err error
725 code string
726 }{{
727- err: errors.NotFoundf("hello"),
728+ err: errors.RawNotFoundf("hello"),
729 code: api.CodeNotFound,
730 }, {
731- err: errors.Unauthorizedf("hello"),
732+ err: errors.RawUnauthorizedf("hello"),
733 code: api.CodeUnauthorized,
734 }, {
735 err: state.ErrCannotEnterScopeYet,
736
737=== modified file 'state/open.go'
738--- state/open.go 2013-06-10 11:16:30 +0000
739+++ state/open.go 2013-06-12 12:32:25 +0000
740@@ -178,19 +178,23 @@
741 logSizeTests = 1000000
742 )
743
744-func maybeUnauthorized(err error, msg string) error {
745+func maybeUnauthorized(err error, message string) error {
746 if err == nil {
747 return nil
748 }
749 // Unauthorized access errors have no error code,
750 // just a simple error string.
751+ // TODO(fwereade): does this comment imply that "auth fails" is a magic
752+ // error value produced my mongo, or mgo, or something? And what about
753+ // the magic "need to login" that's checked in testing/mgo.go? Should
754+ // we be checking for that as well?
755 if err.Error() == "auth fails" {
756- return &errors.UnauthorizedError{err, msg}
757+ return errors.RawUnauthorizedf(message)
758 }
759 if err, ok := err.(*mgo.QueryError); ok && err.Code == 10057 {
760- return &errors.UnauthorizedError{err, msg}
761+ return errors.RawUnauthorizedf(message)
762 }
763- return fmt.Errorf("%s: %v", msg, err)
764+ return fmt.Errorf("%s: %v", message, err)
765 }
766
767 func newState(session *mgo.Session, info *Info) (*State, error) {
768
769=== modified file 'state/state_test.go'
770--- state/state_test.go 2013-06-11 20:56:00 +0000
771+++ state/state_test.go 2013-06-12 12:32:25 +0000
772@@ -79,13 +79,6 @@
773 c.Assert(apiAddrs, DeepEquals, expectedAddrs)
774 }
775
776-func (s *StateSuite) TestIsNotFound(c *C) {
777- err1 := fmt.Errorf("unrelated error")
778- err2 := errors.NotFoundf("foo")
779- c.Assert(errors.IsNotFoundError(err1), Equals, false)
780- c.Assert(errors.IsNotFoundError(err2), Equals, true)
781-}
782-
783 func (s *StateSuite) TestAddCharm(c *C) {
784 // Check that adding charms from scratch works correctly.
785 ch := testing.Charms.Dir("dummy")
786
787=== modified file 'testing/charm.go'
788--- testing/charm.go 2013-06-12 11:25:32 +0000
789+++ testing/charm.go 2013-06-12 12:32:25 +0000
790@@ -6,10 +6,12 @@
791 import (
792 "fmt"
793 "go/build"
794- "launchpad.net/juju-core/charm"
795 "os"
796 "os/exec"
797 "path/filepath"
798+
799+ "launchpad.net/juju-core/charm"
800+ "launchpad.net/juju-core/errors"
801 )
802
803 func init() {
804@@ -169,7 +171,7 @@
805 base, rev := s.interpret(charmURL)
806 charm, found := s.charms[base][rev]
807 if !found {
808- return nil, fmt.Errorf("charm not found in mock store: %s", charmURL)
809+ return nil, errors.RawNotFoundf("charm %q not found in mock store", charmURL)
810 }
811 return charm, nil
812 }
813@@ -179,7 +181,7 @@
814 charmURL = charmURL.WithRevision(-1)
815 base, rev := s.interpret(charmURL)
816 if _, found := s.charms[base][rev]; !found {
817- return 0, fmt.Errorf("charm not found in mock store: %s", charmURL)
818+ return 0, errors.RawNotFoundf("charm %q not found in mock store", charmURL)
819 }
820 return rev, nil
821 }

Subscribers

People subscribed via source and target branches