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  }