1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
11
"github.com/juju/errors"
13
"gopkg.in/juju/names.v2"
14
"gopkg.in/macaroon.v1"
16
"github.com/juju/juju/apiserver/params"
17
"github.com/juju/juju/core/leadership"
18
"github.com/juju/juju/core/lease"
19
"github.com/juju/juju/state"
22
func NotSupportedError(tag names.Tag, operation string) error {
23
return errors.Errorf("entity %q does not support %s", tag, operation)
26
type noAddressSetError struct {
31
func (e *noAddressSetError) Error() string {
32
return fmt.Sprintf("%q has no %s address set", e.unitTag, e.addressName)
35
func NoAddressSetError(unitTag names.UnitTag, addressName string) error {
36
return &noAddressSetError{unitTag: unitTag, addressName: addressName}
39
func isNoAddressSetError(err error) bool {
40
_, ok := err.(*noAddressSetError)
44
type unknownModelError struct {
48
func (e *unknownModelError) Error() string {
49
return fmt.Sprintf("unknown model: %q", e.uuid)
52
func UnknownModelError(uuid string) error {
53
return &unknownModelError{uuid: uuid}
56
func isUnknownModelError(err error) bool {
57
_, ok := err.(*unknownModelError)
61
// DischargeRequiredError is the error returned when a macaroon requires discharging
62
// to complete authentication.
63
type DischargeRequiredError struct {
65
Macaroon *macaroon.Macaroon
68
// Error implements the error interface.
69
func (e *DischargeRequiredError) Error() string {
70
return e.Cause.Error()
73
// IsDischargeRequiredError reports whether the cause
74
// of the error is a *DischargeRequiredError.
75
func IsDischargeRequiredError(err error) bool {
76
_, ok := errors.Cause(err).(*DischargeRequiredError)
80
// IsUpgradeInProgress returns true if this error is caused
81
// by an upgrade in progress.
82
func IsUpgradeInProgressError(err error) bool {
83
if state.IsUpgradeInProgressError(err) {
86
return errors.Cause(err) == params.UpgradeInProgressError
90
ErrBadId = errors.New("id not found")
91
ErrBadCreds = errors.New("invalid entity name or password")
92
ErrLoginExpired = errors.New("login expired")
93
ErrPerm = errors.New("permission denied")
94
ErrNotLoggedIn = errors.New("not logged in")
95
ErrUnknownWatcher = errors.New("unknown watcher id")
96
ErrStoppedWatcher = errors.New("watcher has been stopped")
97
ErrBadRequest = errors.New("invalid request")
98
ErrTryAgain = errors.New("try again")
99
ErrActionNotAvailable = errors.New("action no longer available")
102
// OperationBlockedError returns an error which signifies that
103
// an operation has been blocked; the message should describe
104
// what has been blocked.
105
func OperationBlockedError(msg string) error {
107
msg = "the operation has been blocked"
109
return ¶ms.Error{
111
Code: params.CodeOperationBlocked,
115
var singletonErrorCodes = map[error]string{
116
state.ErrCannotEnterScopeYet: params.CodeCannotEnterScopeYet,
117
state.ErrCannotEnterScope: params.CodeCannotEnterScope,
118
state.ErrUnitHasSubordinates: params.CodeUnitHasSubordinates,
119
state.ErrDead: params.CodeDead,
120
txn.ErrExcessiveContention: params.CodeExcessiveContention,
121
leadership.ErrClaimDenied: params.CodeLeadershipClaimDenied,
122
lease.ErrClaimDenied: params.CodeLeaseClaimDenied,
123
ErrBadId: params.CodeNotFound,
124
ErrBadCreds: params.CodeUnauthorized,
125
ErrLoginExpired: params.CodeLoginExpired,
126
ErrPerm: params.CodeUnauthorized,
127
ErrNotLoggedIn: params.CodeUnauthorized,
128
ErrUnknownWatcher: params.CodeNotFound,
129
ErrStoppedWatcher: params.CodeStopped,
130
ErrTryAgain: params.CodeTryAgain,
131
ErrActionNotAvailable: params.CodeActionNotAvailable,
134
func singletonCode(err error) (string, bool) {
135
// All error types may not be hashable; deal with
136
// that by catching the panic if we try to look up
137
// a non-hashable type.
141
code, ok := singletonErrorCodes[err]
145
func singletonError(err error) (error, bool) {
146
errCode := params.ErrCode(err)
147
for singleton, code := range singletonErrorCodes {
148
if errCode == code && singleton.Error() == err.Error() {
149
return singleton, true
155
// ServerErrorAndStatus is like ServerError but also
156
// returns an HTTP status code appropriate for using
157
// in a response holding the given error.
158
func ServerErrorAndStatus(err error) (*params.Error, int) {
159
err1 := ServerError(err)
161
return nil, http.StatusOK
163
status := http.StatusInternalServerError
165
case params.CodeUnauthorized:
166
status = http.StatusUnauthorized
167
case params.CodeNotFound,
168
params.CodeUserNotFound,
169
params.CodeModelNotFound:
170
status = http.StatusNotFound
171
case params.CodeBadRequest:
172
status = http.StatusBadRequest
173
case params.CodeMethodNotAllowed:
174
status = http.StatusMethodNotAllowed
175
case params.CodeOperationBlocked:
176
// This should really be http.StatusForbidden but earlier versions
177
// of juju clients rely on the 400 status, so we leave it like that.
178
status = http.StatusBadRequest
179
case params.CodeForbidden:
180
status = http.StatusForbidden
181
case params.CodeDischargeRequired:
182
status = http.StatusUnauthorized
183
case params.CodeRetry:
184
status = http.StatusServiceUnavailable
189
// ServerError returns an error suitable for returning to an API
190
// client, with an error code suitable for various kinds of errors
191
// generated in packages outside the API.
192
func ServerError(err error) *params.Error {
197
// Skip past annotations when looking for the code.
198
err = errors.Cause(err)
199
code, ok := singletonCode(err)
200
var info *params.ErrorInfo
203
case isIOTimeout(err):
204
code = params.CodeRetry
205
case errors.IsUnauthorized(err):
206
code = params.CodeUnauthorized
207
case errors.IsNotFound(err):
208
code = params.CodeNotFound
209
case errors.IsUserNotFound(err):
210
code = params.CodeUserNotFound
211
case errors.IsAlreadyExists(err):
212
code = params.CodeAlreadyExists
213
case errors.IsNotAssigned(err):
214
code = params.CodeNotAssigned
215
case state.IsHasAssignedUnitsError(err):
216
code = params.CodeHasAssignedUnits
217
case state.IsHasHostedModelsError(err):
218
code = params.CodeHasHostedModels
219
case isNoAddressSetError(err):
220
code = params.CodeNoAddressSet
221
case errors.IsNotProvisioned(err):
222
code = params.CodeNotProvisioned
223
case IsUpgradeInProgressError(err):
224
code = params.CodeUpgradeInProgress
225
case state.IsHasAttachmentsError(err):
226
code = params.CodeMachineHasAttachedStorage
227
case isUnknownModelError(err):
228
code = params.CodeModelNotFound
229
case errors.IsNotSupported(err):
230
code = params.CodeNotSupported
231
case errors.IsBadRequest(err):
232
code = params.CodeBadRequest
233
case errors.IsMethodNotAllowed(err):
234
code = params.CodeMethodNotAllowed
236
if err, ok := err.(*DischargeRequiredError); ok {
237
code = params.CodeDischargeRequired
238
info = ¶ms.ErrorInfo{
239
Macaroon: err.Macaroon,
240
// One macaroon fits all.
245
code = params.ErrCode(err)
247
return ¶ms.Error{
254
// Unfortunately there is no specific type of error for i/o timeout,
255
// and the error that bubbles up from mgo is annotated and a string type,
256
// so all we can do is look at the error suffix and see if it matches.
257
func isIOTimeout(err error) bool {
258
// Perhaps sometime in the future, we'll have additional ways to tell if
259
// the error is an i/o timeout type error, but for now this is all we
262
return strings.HasSuffix(msg, "i/o timeout")
265
func DestroyErr(desc string, ids, errs []string) error {
266
// TODO(waigani) refactor DestroyErr to take a map of ids to errors.
270
msg := "some %s were not destroyed"
271
if len(errs) == len(ids) {
272
msg = "no %s were destroyed"
274
msg = fmt.Sprintf(msg, desc)
275
return errors.Errorf("%s: %s", msg, strings.Join(errs, "; "))
278
// RestoreError makes a best effort at converting the given error
279
// back into an error originally converted by ServerError().
280
func RestoreError(err error) error {
281
err = errors.Cause(err)
283
if apiErr, ok := err.(*params.Error); !ok {
285
} else if apiErr == nil {
288
if params.ErrCode(err) == "" {
293
if singleton, ok := singletonError(err); ok {
297
// TODO(ericsnow) Support the other error types handled by ServerError().
299
case params.IsCodeUnauthorized(err):
300
return errors.NewUnauthorized(nil, msg)
301
case params.IsCodeNotFound(err):
302
// TODO(ericsnow) UnknownModelError should be handled here too.
303
// ...by parsing msg?
304
return errors.NewNotFound(nil, msg)
305
case params.IsCodeUserNotFound(err):
306
return errors.NewUserNotFound(nil, msg)
307
case params.IsCodeAlreadyExists(err):
308
return errors.NewAlreadyExists(nil, msg)
309
case params.IsCodeNotAssigned(err):
310
return errors.NewNotAssigned(nil, msg)
311
case params.IsCodeHasAssignedUnits(err):
312
// TODO(ericsnow) Handle state.HasAssignedUnitsError here.
313
// ...by parsing msg?
315
case params.IsCodeHasHostedModels(err):
317
case params.IsCodeNoAddressSet(err):
318
// TODO(ericsnow) Handle isNoAddressSetError here.
319
// ...by parsing msg?
321
case params.IsCodeNotProvisioned(err):
322
return errors.NewNotProvisioned(nil, msg)
323
case params.IsCodeUpgradeInProgress(err):
324
// TODO(ericsnow) Handle state.UpgradeInProgressError here.
325
// ...by parsing msg?
327
case params.IsCodeMachineHasAttachedStorage(err):
328
// TODO(ericsnow) Handle state.HasAttachmentsError here.
329
// ...by parsing msg?
331
case params.IsCodeNotSupported(err):
332
return errors.NewNotSupported(nil, msg)
333
case params.IsBadRequest(err):
334
return errors.NewBadRequest(nil, msg)
335
case params.IsMethodNotAllowed(err):
336
return errors.NewMethodNotAllowed(nil, msg)
337
case params.ErrCode(err) == params.CodeDischargeRequired:
338
// TODO(ericsnow) Handle DischargeRequiredError here.