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