github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/common/errors_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common_test 5 6 import ( 7 stderrors "errors" 8 "net/http" 9 10 "github.com/juju/errors" 11 "github.com/juju/names" 12 jc "github.com/juju/testing/checkers" 13 "github.com/juju/txn" 14 gc "gopkg.in/check.v1" 15 "gopkg.in/macaroon.v1" 16 17 "github.com/juju/juju/apiserver/common" 18 "github.com/juju/juju/apiserver/params" 19 "github.com/juju/juju/core/leadership" 20 "github.com/juju/juju/core/lease" 21 "github.com/juju/juju/state" 22 "github.com/juju/juju/testing" 23 ) 24 25 type errorsSuite struct { 26 testing.BaseSuite 27 } 28 29 var _ = gc.Suite(&errorsSuite{}) 30 31 var errorTransformTests = []struct { 32 err error 33 code string 34 status int 35 helperFunc func(error) bool 36 }{{ 37 err: errors.NotFoundf("hello"), 38 code: params.CodeNotFound, 39 status: http.StatusNotFound, 40 helperFunc: params.IsCodeNotFound, 41 }, { 42 err: errors.Unauthorizedf("hello"), 43 code: params.CodeUnauthorized, 44 status: http.StatusUnauthorized, 45 helperFunc: params.IsCodeUnauthorized, 46 }, { 47 err: state.ErrCannotEnterScopeYet, 48 code: params.CodeCannotEnterScopeYet, 49 status: http.StatusInternalServerError, 50 helperFunc: params.IsCodeCannotEnterScopeYet, 51 }, { 52 err: state.ErrCannotEnterScope, 53 code: params.CodeCannotEnterScope, 54 status: http.StatusInternalServerError, 55 helperFunc: params.IsCodeCannotEnterScope, 56 }, { 57 err: state.ErrDead, 58 code: params.CodeDead, 59 status: http.StatusInternalServerError, 60 helperFunc: params.IsCodeDead, 61 }, { 62 err: txn.ErrExcessiveContention, 63 code: params.CodeExcessiveContention, 64 status: http.StatusInternalServerError, 65 helperFunc: params.IsCodeExcessiveContention, 66 }, { 67 err: state.ErrUnitHasSubordinates, 68 code: params.CodeUnitHasSubordinates, 69 status: http.StatusInternalServerError, 70 helperFunc: params.IsCodeUnitHasSubordinates, 71 }, { 72 err: common.ErrBadId, 73 code: params.CodeNotFound, 74 status: http.StatusNotFound, 75 helperFunc: params.IsCodeNotFound, 76 }, { 77 err: common.NoAddressSetError(names.NewUnitTag("mysql/0"), "public"), 78 code: params.CodeNoAddressSet, 79 status: http.StatusInternalServerError, 80 helperFunc: params.IsCodeNoAddressSet, 81 }, { 82 err: common.ErrBadCreds, 83 code: params.CodeUnauthorized, 84 status: http.StatusUnauthorized, 85 helperFunc: params.IsCodeUnauthorized, 86 }, { 87 err: common.ErrPerm, 88 code: params.CodeUnauthorized, 89 status: http.StatusUnauthorized, 90 helperFunc: params.IsCodeUnauthorized, 91 }, { 92 err: common.ErrNotLoggedIn, 93 code: params.CodeUnauthorized, 94 status: http.StatusUnauthorized, 95 helperFunc: params.IsCodeUnauthorized, 96 }, { 97 err: errors.NotProvisionedf("machine 0"), 98 code: params.CodeNotProvisioned, 99 status: http.StatusInternalServerError, 100 helperFunc: params.IsCodeNotProvisioned, 101 }, { 102 err: errors.AlreadyExistsf("blah"), 103 code: params.CodeAlreadyExists, 104 status: http.StatusInternalServerError, 105 helperFunc: params.IsCodeAlreadyExists, 106 }, { 107 err: common.ErrUnknownWatcher, 108 code: params.CodeNotFound, 109 status: http.StatusNotFound, 110 helperFunc: params.IsCodeNotFound, 111 }, { 112 err: errors.NotAssignedf("unit mysql/0"), 113 code: params.CodeNotAssigned, 114 status: http.StatusInternalServerError, 115 helperFunc: params.IsCodeNotAssigned, 116 }, { 117 err: common.ErrStoppedWatcher, 118 code: params.CodeStopped, 119 status: http.StatusInternalServerError, 120 helperFunc: params.IsCodeStopped, 121 }, { 122 err: &state.HasAssignedUnitsError{"42", []string{"a"}}, 123 code: params.CodeHasAssignedUnits, 124 status: http.StatusInternalServerError, 125 helperFunc: params.IsCodeHasAssignedUnits, 126 }, { 127 err: common.ErrTryAgain, 128 code: params.CodeTryAgain, 129 status: http.StatusInternalServerError, 130 helperFunc: params.IsCodeTryAgain, 131 }, { 132 err: leadership.ErrClaimDenied, 133 code: params.CodeLeadershipClaimDenied, 134 status: http.StatusInternalServerError, 135 helperFunc: params.IsCodeLeadershipClaimDenied, 136 }, { 137 err: lease.ErrClaimDenied, 138 code: params.CodeLeaseClaimDenied, 139 status: http.StatusInternalServerError, 140 helperFunc: params.IsCodeLeaseClaimDenied, 141 }, { 142 err: common.OperationBlockedError("test"), 143 code: params.CodeOperationBlocked, 144 status: http.StatusBadRequest, 145 helperFunc: params.IsCodeOperationBlocked, 146 }, { 147 err: errors.NotSupportedf("needed feature"), 148 code: params.CodeNotSupported, 149 status: http.StatusInternalServerError, 150 helperFunc: params.IsCodeNotSupported, 151 }, { 152 err: errors.BadRequestf("something"), 153 code: params.CodeBadRequest, 154 status: http.StatusBadRequest, 155 helperFunc: params.IsBadRequest, 156 }, { 157 err: errors.MethodNotAllowedf("something"), 158 code: params.CodeMethodNotAllowed, 159 status: http.StatusMethodNotAllowed, 160 helperFunc: params.IsMethodNotAllowed, 161 }, { 162 err: stderrors.New("an error"), 163 status: http.StatusInternalServerError, 164 code: "", 165 }, { 166 err: &common.DischargeRequiredError{ 167 Cause: errors.New("something"), 168 Macaroon: sampleMacaroon, 169 }, 170 status: http.StatusUnauthorized, 171 code: params.CodeDischargeRequired, 172 helperFunc: func(err error) bool { 173 err1, ok := err.(*params.Error) 174 if !ok || err1.Info == nil || err1.Info.Macaroon != sampleMacaroon { 175 return false 176 } 177 return true 178 }, 179 }, { 180 err: unhashableError{"foo"}, 181 status: http.StatusInternalServerError, 182 code: "", 183 }, { 184 err: common.UnknownModelError("dead-beef-123456"), 185 code: params.CodeNotFound, 186 status: http.StatusNotFound, 187 helperFunc: params.IsCodeNotFound, 188 }, { 189 err: nil, 190 code: "", 191 status: http.StatusOK, 192 }} 193 194 var sampleMacaroon = func() *macaroon.Macaroon { 195 m, err := macaroon.New([]byte("key"), "id", "loc") 196 if err != nil { 197 panic(err) 198 } 199 return m 200 }() 201 202 type unhashableError []string 203 204 func (err unhashableError) Error() string { 205 return err[0] 206 } 207 208 func (s *errorsSuite) TestErrorTransform(c *gc.C) { 209 for i, t := range errorTransformTests { 210 c.Logf("running test %d: %T{%q}", i, t.err, t.err) 211 err1, status := common.ServerErrorAndStatus(t.err) 212 213 // Sanity check that ServerError returns the same thing. 214 err2 := common.ServerError(t.err) 215 c.Assert(err2, gc.DeepEquals, err1) 216 c.Assert(status, gc.Equals, t.status) 217 218 if t.err == nil { 219 c.Assert(err1, gc.IsNil) 220 c.Assert(status, gc.Equals, http.StatusOK) 221 continue 222 } 223 c.Assert(err1.Message, gc.Equals, t.err.Error()) 224 c.Assert(err1.Code, gc.Equals, t.code) 225 if t.helperFunc != nil { 226 c.Assert(err1, jc.Satisfies, t.helperFunc) 227 } 228 229 // TODO(ericsnow) Remove this switch once the other error types are supported. 230 switch t.code { 231 case params.CodeHasAssignedUnits, 232 params.CodeNoAddressSet, 233 params.CodeUpgradeInProgress, 234 params.CodeMachineHasAttachedStorage, 235 params.CodeDischargeRequired: 236 continue 237 case params.CodeNotFound: 238 if common.IsUnknownModelError(t.err) { 239 continue 240 } 241 case params.CodeOperationBlocked: 242 // ServerError doesn't actually have a case for this code. 243 continue 244 } 245 246 c.Logf(" checking restore (%#v)", err1) 247 restored := common.RestoreError(err1) 248 if t.err == nil { 249 c.Check(restored, jc.ErrorIsNil) 250 } else if t.code == "" { 251 c.Check(restored.Error(), gc.Equals, t.err.Error()) 252 } else { 253 // TODO(ericsnow) Use a stricter DeepEquals check. 254 c.Check(errors.Cause(restored), gc.FitsTypeOf, t.err) 255 c.Check(restored.Error(), gc.Equals, t.err.Error()) 256 } 257 } 258 } 259 260 func (s *errorsSuite) TestUnknownModel(c *gc.C) { 261 err := common.UnknownModelError("dead-beef") 262 c.Check(err, gc.ErrorMatches, `unknown model: "dead-beef"`) 263 } 264 265 func (s *errorsSuite) TestDestroyErr(c *gc.C) { 266 errs := []string{ 267 "error one", 268 "error two", 269 "error three", 270 } 271 ids := []string{ 272 "id1", 273 "id2", 274 "id3", 275 } 276 277 c.Assert(common.DestroyErr("entities", ids, nil), jc.ErrorIsNil) 278 279 err := common.DestroyErr("entities", ids, errs) 280 c.Assert(err, gc.ErrorMatches, "no entities were destroyed: error one; error two; error three") 281 282 err = common.DestroyErr("entities", ids, errs[1:]) 283 c.Assert(err, gc.ErrorMatches, "some entities were not destroyed: error two; error three") 284 }