Example #1
0
// RestoreError makes a best effort at converting the given error
// back into an error originally converted by ServerError(). If the
// error could not be converted then false is returned.
func RestoreError(err error) (error, bool) {
	err = errors.Cause(err)

	if apiErr, ok := err.(*params.Error); !ok {
		return err, false
	} else if apiErr == nil {
		return nil, true
	}
	if params.ErrCode(err) == "" {
		return err, false
	}
	msg := err.Error()

	if singleton, ok := singletonError(err); ok {
		return singleton, true
	}

	// TODO(ericsnow) Support the other error types handled by ServerError().
	switch {
	case params.IsCodeUnauthorized(err):
		return errors.NewUnauthorized(nil, msg), true
	case params.IsCodeNotFound(err):
		// TODO(ericsnow) UnknownModelError should be handled here too.
		// ...by parsing msg?
		return errors.NewNotFound(nil, msg), true
	case params.IsCodeAlreadyExists(err):
		return errors.NewAlreadyExists(nil, msg), true
	case params.IsCodeNotAssigned(err):
		return errors.NewNotAssigned(nil, msg), true
	case params.IsCodeHasAssignedUnits(err):
		// TODO(ericsnow) Handle state.HasAssignedUnitsError here.
		// ...by parsing msg?
		return err, false
	case params.IsCodeNoAddressSet(err):
		// TODO(ericsnow) Handle isNoAddressSetError here.
		// ...by parsing msg?
		return err, false
	case params.IsCodeNotProvisioned(err):
		return errors.NewNotProvisioned(nil, msg), true
	case params.IsCodeUpgradeInProgress(err):
		// TODO(ericsnow) Handle state.UpgradeInProgressError here.
		// ...by parsing msg?
		return err, false
	case params.IsCodeMachineHasAttachedStorage(err):
		// TODO(ericsnow) Handle state.HasAttachmentsError here.
		// ...by parsing msg?
		return err, false
	case params.IsCodeNotSupported(err):
		return errors.NewNotSupported(nil, msg), true
	case params.IsBadRequest(err):
		return errors.NewBadRequest(nil, msg), true
	case params.IsMethodNotAllowed(err):
		return errors.NewMethodNotAllowed(nil, msg), true
	case params.ErrCode(err) == params.CodeDischargeRequired:
		// TODO(ericsnow) Handle DischargeRequiredError here.
		return err, false
	default:
		return err, false
	}
}
Example #2
0
func (s *deployerSuite) TestUnitRemove(c *gc.C) {
	unit, err := s.st.Unit(s.principal.Tag().(names.UnitTag))
	c.Assert(err, jc.ErrorIsNil)

	// It fails because the entity is still alive.
	// And EnsureDead will fail because there is a subordinate.
	err = unit.Remove()
	c.Assert(err, gc.ErrorMatches, `cannot remove entity "unit-mysql-0": still alive`)
	c.Assert(params.ErrCode(err), gc.Equals, "")

	// With the subordinate it also fails due to it being alive.
	unit, err = s.st.Unit(s.subordinate.Tag().(names.UnitTag))
	c.Assert(err, jc.ErrorIsNil)
	err = unit.Remove()
	c.Assert(err, gc.ErrorMatches, `cannot remove entity "unit-logging-0": still alive`)
	c.Assert(params.ErrCode(err), gc.Equals, "")

	// Make it dead first and try again.
	err = s.subordinate.EnsureDead()
	c.Assert(err, jc.ErrorIsNil)
	err = unit.Remove()
	c.Assert(err, jc.ErrorIsNil)

	// Verify it's gone.
	err = unit.Refresh()
	s.assertUnauthorized(c, err)
	unit, err = s.st.Unit(s.subordinate.Tag().(names.UnitTag))
	s.assertUnauthorized(c, err)
	c.Assert(unit, gc.IsNil)
}
Example #3
0
func (*errorSuite) TestErrCode(c *gc.C) {
	var err error
	err = &params.Error{Code: params.CodeDead, Message: "brain dead test"}
	c.Check(params.ErrCode(err), gc.Equals, params.CodeDead)

	err = errors.Trace(err)
	c.Check(params.ErrCode(err), gc.Equals, params.CodeDead)
}
Example #4
0
// ServerError returns an error suitable for returning to an API
// client, with an error code suitable for various kinds of errors
// generated in packages outside the API.
func ServerError(err error) *params.Error {
	if err == nil {
		return nil
	}
	code, ok := singletonCode(err)
	switch {
	case ok:
	case errors.IsUnauthorized(err):
		code = params.CodeUnauthorized
	case errors.IsNotFound(err):
		code = params.CodeNotFound
	case errors.IsAlreadyExists(err):
		code = params.CodeAlreadyExists
	case state.IsNotAssigned(err):
		code = params.CodeNotAssigned
	case state.IsHasAssignedUnitsError(err):
		code = params.CodeHasAssignedUnits
	case IsNoAddressSetError(err):
		code = params.CodeNoAddressSet
	case state.IsNotProvisionedError(err):
		code = params.CodeNotProvisioned
	case IsUnknownEnviromentError(err):
		code = params.CodeNotFound
	default:
		code = params.ErrCode(err)
	}
	return &params.Error{
		Message: err.Error(),
		Code:    code,
	}
}
Example #5
0
func (s *httpSuite) TestHTTPClient(c *gc.C) {
	var handler http.HandlerFunc
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		handler(w, req)
	}))
	defer srv.Close()
	s.client.BaseURL = srv.URL
	for i, test := range httpClientTests {
		c.Logf("test %d: %s", i, test.about)
		handler = test.handler
		var resp interface{}
		if test.expectResponse != nil {
			resp = reflect.New(reflect.TypeOf(test.expectResponse).Elem()).Interface()
		}
		err := s.client.Get("/", resp)
		if test.expectError != "" {
			c.Check(err, gc.ErrorMatches, test.expectError)
			c.Check(params.ErrCode(err), gc.Equals, test.expectErrorCode)
			if err, ok := errors.Cause(err).(*params.Error); ok {
				c.Check(err.Info, jc.DeepEquals, test.expectErrorInfo)
			} else if test.expectErrorInfo != nil {
				c.Fatalf("no error info found in error")
			}
			continue
		}
		c.Check(err, gc.IsNil)
		c.Check(resp, jc.DeepEquals, test.expectResponse)
	}
}
Example #6
0
File: errors.go Project: bac/juju
// ServerError returns an error suitable for returning to an API
// client, with an error code suitable for various kinds of errors
// generated in packages outside the API.
func ServerError(err error) *params.Error {
	if err == nil {
		return nil
	}
	logger.Tracef("server RPC error %v", errors.Details(err))
	msg := err.Error()
	// Skip past annotations when looking for the code.
	err = errors.Cause(err)
	code, ok := singletonCode(err)
	var info *params.ErrorInfo
	switch {
	case ok:
	case errors.IsUnauthorized(err):
		code = params.CodeUnauthorized
	case errors.IsNotFound(err):
		code = params.CodeNotFound
	case errors.IsUserNotFound(err):
		code = params.CodeUserNotFound
	case errors.IsAlreadyExists(err):
		code = params.CodeAlreadyExists
	case errors.IsNotAssigned(err):
		code = params.CodeNotAssigned
	case state.IsHasAssignedUnitsError(err):
		code = params.CodeHasAssignedUnits
	case state.IsHasHostedModelsError(err):
		code = params.CodeHasHostedModels
	case isNoAddressSetError(err):
		code = params.CodeNoAddressSet
	case errors.IsNotProvisioned(err):
		code = params.CodeNotProvisioned
	case IsUpgradeInProgressError(err):
		code = params.CodeUpgradeInProgress
	case state.IsHasAttachmentsError(err):
		code = params.CodeMachineHasAttachedStorage
	case isUnknownModelError(err):
		code = params.CodeModelNotFound
	case errors.IsNotSupported(err):
		code = params.CodeNotSupported
	case errors.IsBadRequest(err):
		code = params.CodeBadRequest
	case errors.IsMethodNotAllowed(err):
		code = params.CodeMethodNotAllowed
	default:
		if err, ok := err.(*DischargeRequiredError); ok {
			code = params.CodeDischargeRequired
			info = &params.ErrorInfo{
				Macaroon: err.Macaroon,
				// One macaroon fits all.
				MacaroonPath: "/",
			}
			break
		}
		code = params.ErrCode(err)
	}
	return &params.Error{
		Message: msg,
		Code:    code,
		Info:    info,
	}
}
Example #7
0
// ConnectStream implements StreamConnector.ConnectStream.
func (st *state) ConnectStream(path string, attrs url.Values) (base.Stream, error) {
	if !st.isLoggedIn() {
		return nil, errors.New("cannot use ConnectStream without logging in")
	}
	// We use the standard "macaraq" macaroon authentication dance here.
	// That is, we attach any macaroons we have to the initial request,
	// and if that succeeds, all's good. If it fails with a DischargeRequired
	// error, the response will contain a macaroon that, when discharged,
	// may allow access, so we discharge it (using bakery.Client.HandleError)
	// and try the request again.
	conn, err := st.connectStream(path, attrs)
	if err == nil {
		return conn, err
	}
	if params.ErrCode(err) != params.CodeDischargeRequired {
		return nil, errors.Trace(err)
	}
	if err := st.bakeryClient.HandleError(st.cookieURL, bakeryError(err)); err != nil {
		return nil, errors.Trace(err)
	}
	// Try again with the discharged macaroon.
	conn, err = st.connectStream(path, attrs)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return conn, nil
}
Example #8
0
File: base.go Project: bac/juju
// NewAPIRoot returns a new connection to the API server for the given
// model or controller.
func (c *JujuCommandBase) NewAPIRoot(
	store jujuclient.ClientStore,
	controllerName, modelName string,
) (api.Connection, error) {
	accountDetails, err := store.AccountDetails(controllerName)
	if err != nil && !errors.IsNotFound(err) {
		return nil, errors.Trace(err)
	}
	// If there are no account details or there's no logged-in
	// user or the user is external, then trigger macaroon authentication
	// by using an empty AccountDetails.
	if accountDetails == nil || accountDetails.User == "" {
		accountDetails = &jujuclient.AccountDetails{}
	} else {
		u := names.NewUserTag(accountDetails.User)
		if !u.IsLocal() {
			accountDetails = &jujuclient.AccountDetails{}
		}
	}
	param, err := c.NewAPIConnectionParams(
		store, controllerName, modelName, accountDetails,
	)
	if err != nil {
		return nil, errors.Trace(err)
	}
	conn, err := juju.NewAPIConnection(param)
	if modelName != "" && params.ErrCode(err) == params.CodeModelNotFound {
		return nil, c.missingModelError(store, controllerName, modelName)
	}
	return conn, err
}
Example #9
0
func (s *apiclientSuite) TestOpenHonorsEnvironTag(c *gc.C) {
	info := s.APIInfo(c)

	// TODO(jam): 2014-06-05 http://pad.lv/1326802
	// we want to test this eventually, but for now s.APIInfo uses
	// conn.StateInfo() which doesn't know about EnvironTag.
	// c.Check(info.EnvironTag, gc.Equals, env.Tag())
	// c.Assert(info.EnvironTag, gc.Not(gc.Equals), "")

	// We start by ensuring we have an invalid tag, and Open should fail.
	info.EnvironTag = names.NewEnvironTag("bad-tag")
	_, err := api.Open(info, api.DialOpts{})
	c.Check(err, gc.ErrorMatches, `unknown environment: "bad-tag"`)
	c.Check(params.ErrCode(err), gc.Equals, params.CodeNotFound)

	// Now set it to the right tag, and we should succeed.
	info.EnvironTag = s.State.EnvironTag()
	st, err := api.Open(info, api.DialOpts{})
	c.Assert(err, jc.ErrorIsNil)
	st.Close()

	// Backwards compatibility, we should succeed if we do not set an
	// environ tag
	info.EnvironTag = names.NewEnvironTag("")
	st, err = api.Open(info, api.DialOpts{})
	c.Assert(err, jc.ErrorIsNil)
	st.Close()
}
Example #10
0
// UploadTools uploads tools at the specified location to the API server over HTTPS.
func (c *Client) UploadTools(r io.ReadSeeker, vers version.Binary, additionalSeries ...string) (*tools.Tools, error) {
	endpoint := fmt.Sprintf("/tools?binaryVersion=%s&series=%s", vers, strings.Join(additionalSeries, ","))

	req, err := http.NewRequest("POST", endpoint, nil)
	if err != nil {
		return nil, errors.Annotate(err, "cannot create upload request")
	}
	req.Header.Set("Content-Type", "application/x-tar-gz")

	httpClient, err := c.st.HTTPClient()
	if err != nil {
		return nil, errors.Trace(err)
	}
	var resp params.ToolsResult
	err = httpClient.Do(req, r, &resp)
	if err != nil {
		msg := err.Error()
		if params.ErrCode(err) == "" && strings.Contains(msg, params.CodeOperationBlocked) {
			// We're probably talking to an old version of the API server
			// that doesn't provide error codes.
			// See https://bugs.launchpad.net/juju-core/+bug/1499277
			err = &params.Error{
				Code:    params.CodeOperationBlocked,
				Message: msg,
			}
		}
		return nil, errors.Trace(err)
	}
	return resp.Tools, nil
}
Example #11
0
File: errors.go Project: makyo/juju
func singletonError(err error) (error, bool) {
	errCode := params.ErrCode(err)
	for singleton, code := range singletonErrorCodes {
		if errCode == code && singleton.Error() == err.Error() {
			return singleton, true
		}
	}
	return nil, false
}
Example #12
0
func (s *loginSuite) TestBadLogin(c *gc.C) {
	// Start our own server so we can control when the first login
	// happens. Otherwise in JujuConnSuite.SetUpTest api.Open is
	// called with user-admin permissions automatically.
	info, cleanup := s.setupServerWithValidator(c, nil)
	defer cleanup()

	adminUser := s.AdminUserTag(c)

	for i, t := range []struct {
		tag      names.Tag
		password string
		err      error
		code     string
	}{{
		tag:      adminUser,
		password: "******",
		err: &rpc.RequestError{
			Message: "invalid entity name or password",
			Code:    "unauthorized access",
		},
		code: params.CodeUnauthorized,
	}, {
		tag:      names.NewUserTag("unknown"),
		password: "******",
		err: &rpc.RequestError{
			Message: "invalid entity name or password",
			Code:    "unauthorized access",
		},
		code: params.CodeUnauthorized,
	}} {
		c.Logf("test %d; entity %q; password %q", i, t.tag, t.password)
		func() {
			// Open the API without logging in, so we can perform
			// operations on the connection before calling Login.
			st := s.openAPIWithoutLogin(c, info)
			defer st.Close()

			_, err := apimachiner.NewState(st).Machine(names.NewMachineTag("0"))
			c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{
				Message: `unknown object type "Machiner"`,
				Code:    "not implemented",
			})

			// Since these are user login tests, the nonce is empty.
			err = st.Login(t.tag, t.password, "", nil)
			c.Assert(errors.Cause(err), gc.DeepEquals, t.err)
			c.Assert(params.ErrCode(err), gc.Equals, t.code)

			_, err = apimachiner.NewState(st).Machine(names.NewMachineTag("0"))
			c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{
				Message: `unknown object type "Machiner"`,
				Code:    "not implemented",
			})
		}()
	}
}
Example #13
0
// WaitForAgentInitialisation polls the bootstrapped controller with a read-only
// command which will fail until the controller is fully initialised.
// TODO(wallyworld) - add a bespoke command to maybe the admin facade for this purpose.
func WaitForAgentInitialisation(ctx *cmd.Context, c *modelcmd.ModelCommandBase, controllerName, hostedModelName string) error {
	// TODO(katco): 2016-08-09: lp:1611427
	attempts := utils.AttemptStrategy{
		Min:   bootstrapReadyPollCount,
		Delay: bootstrapReadyPollDelay,
	}
	var (
		apiAttempts int
		err         error
	)

	// Make a best effort to find the new controller address so we can print it.
	addressInfo := ""
	controller, err := c.ClientStore().ControllerByName(controllerName)
	if err == nil && len(controller.APIEndpoints) > 0 {
		addr, err := network.ParseHostPort(controller.APIEndpoints[0])
		if err == nil {
			addressInfo = fmt.Sprintf(" at %s", addr.Address.Value)
		}
	}

	ctx.Infof("Contacting Juju controller%s to verify accessibility...", addressInfo)
	apiAttempts = 1
	for attempt := attempts.Start(); attempt.Next(); apiAttempts++ {
		err = tryAPI(c)
		if err == nil {
			ctx.Infof("Bootstrap complete, %q controller now available.", controllerName)
			ctx.Infof("Controller machines are in the %q model.", bootstrap.ControllerModelName)
			ctx.Infof("Initial model %q added.", hostedModelName)
			break
		}
		// As the API server is coming up, it goes through a number of steps.
		// Initially the upgrade steps run, but the api server allows some
		// calls to be processed during the upgrade, but not the list blocks.
		// Logins are also blocked during space discovery.
		// It is also possible that the underlying database causes connections
		// to be dropped as it is initialising, or reconfiguring. These can
		// lead to EOF or "connection is shut down" error messages. We skip
		// these too, hoping that things come back up before the end of the
		// retry poll count.
		errorMessage := errors.Cause(err).Error()
		switch {
		case errors.Cause(err) == io.EOF,
			strings.HasSuffix(errorMessage, "connection is shut down"),
			strings.HasSuffix(errorMessage, "no api connection available"),
			strings.Contains(errorMessage, "spaces are still being discovered"):
			ctx.Verbosef("Still waiting for API to become available")
			continue
		case params.ErrCode(err) == params.CodeUpgradeInProgress:
			ctx.Verbosef("Still waiting for API to become available: %v", err)
			continue
		}
		break
	}
	return errors.Annotatef(err, "unable to contact api server after %d attempts", apiAttempts)
}
Example #14
0
func (s *loginSuite) TestBadLogin(c *gc.C) {
	// Start our own server so we can control when the first login
	// happens. Otherwise in JujuConnSuite.SetUpTest api.Open is
	// called with user-admin permissions automatically.
	info, cleanup := s.setupServer(c)
	defer cleanup()

	adminUser := s.AdminUserTag(c)

	for i, t := range []struct {
		tag      string
		password string
		err      string
		code     string
	}{{
		tag:      adminUser.String(),
		password: "******",
		err:      "invalid entity name or password",
		code:     params.CodeUnauthorized,
	}, {
		tag:      "user-unknown",
		password: "******",
		err:      "invalid entity name or password",
		code:     params.CodeUnauthorized,
	}, {
		tag:      "bar",
		password: "******",
		err:      `"bar" is not a valid tag`,
	}} {
		c.Logf("test %d; entity %q; password %q", i, t.tag, t.password)
		// Note that Open does not log in if the tag and password
		// are empty. This allows us to test operations on the connection
		// before calling Login, which we could not do if Open
		// always logged in.
		info.Tag = nil
		info.Password = ""
		func() {
			st, err := api.Open(info, fastDialOpts)
			c.Assert(err, gc.IsNil)
			defer st.Close()

			_, err = st.Machiner().Machine(names.NewMachineTag("0"))
			c.Assert(err, gc.ErrorMatches, `unknown object type "Machiner"`)

			// Since these are user login tests, the nonce is empty.
			err = st.Login(t.tag, t.password, "")
			c.Assert(err, gc.ErrorMatches, t.err)
			c.Assert(params.ErrCode(err), gc.Equals, t.code)

			_, err = st.Machiner().Machine(names.NewMachineTag("0"))
			c.Assert(err, gc.ErrorMatches, `unknown object type "Machiner"`)
		}()
	}
}
Example #15
0
func (c *showControllerCommand) agentVersion(client ControllerAccessAPI, ctx *cmd.Context) string {
	var ver string
	mc, err := client.ModelConfig()
	if err != nil {
		code := params.ErrCode(err)
		if code != "" {
			ver = fmt.Sprintf("(%s)", code)
		} else {
			fmt.Fprintln(ctx.Stderr, err)
			ver = "(error)"
		}
		return ver
	}
	return mc["agent-version"].(string)
}
Example #16
0
func (c *showControllerCommand) userAccess(client ControllerAccessAPI, ctx *cmd.Context, user string) string {
	var access string
	userAccess, err := client.GetControllerAccess(user)
	if err == nil {
		access = string(userAccess)
	} else {
		code := params.ErrCode(err)
		if code != "" {
			access = fmt.Sprintf("(%s)", code)
		} else {
			fmt.Fprintln(ctx.Stderr, err)
			access = "(error)"
		}
	}
	return access
}
Example #17
0
// bakeryError translates any discharge-required error into
// an error value that the httpbakery package will recognize.
// Other errors are returned unchanged.
func bakeryError(err error) error {
	if params.ErrCode(err) != params.CodeDischargeRequired {
		return err
	}
	errResp := errors.Cause(err).(*params.Error)
	if errResp.Info == nil {
		return errors.Annotatef(err, "no error info found in discharge-required response error")
	}
	// It's a discharge-required error, so make an appropriate httpbakery
	// error from it.
	return &httpbakery.Error{
		Message: err.Error(),
		Code:    httpbakery.ErrDischargeRequired,
		Info: &httpbakery.ErrorInfo{
			Macaroon:     errResp.Info.Macaroon,
			MacaroonPath: errResp.Info.MacaroonPath,
		},
	}
}
Example #18
0
func (s *keyManagerSuite) TestAddJujuSystemKeyNotMachine(c *gc.C) {
	anAuthoriser := s.authoriser
	anAuthoriser.EnvironManager = true
	anAuthoriser.Tag = names.NewUnitTag("wordpress/0")
	var err error
	s.keymanager, err = keymanager.NewKeyManagerAPI(s.State, s.resources, anAuthoriser)
	c.Assert(err, jc.ErrorIsNil)
	key1 := sshtesting.ValidKeyOne.Key
	s.setAuthorisedKeys(c, key1)

	newKey := sshtesting.ValidKeyThree.Key + " juju-system-key"
	args := params.ModifyUserSSHKeys{
		User: "******",
		Keys: []string{newKey},
	}
	_, err = s.keymanager.AddKeys(args)
	c.Assert(err, gc.ErrorMatches, "permission denied")
	c.Assert(params.ErrCode(err), gc.Equals, params.CodeUnauthorized)
	s.assertEnvironKeys(c, []string{key1})
}
Example #19
0
File: destroy.go Project: bac/juju
// newTimedModelStatus returns a function which waits a given period of time
// before querying the API server for the status of a model.
func newTimedModelStatus(ctx *cmd.Context, api DestroyModelAPI, tag names.ModelTag, sleepFunc func(time.Duration)) func(time.Duration) *modelData {
	return func(wait time.Duration) *modelData {
		sleepFunc(wait)
		status, err := api.ModelStatus(tag)
		if err != nil {
			if params.ErrCode(err) != params.CodeNotFound {
				ctx.Infof("Unable to get the model status from the API: %v.", err)
			}
			return nil
		}
		if l := len(status); l != 1 {
			ctx.Infof("error finding model status: expected one result, got %d", l)
			return nil
		}
		return &modelData{
			machineCount:     status[0].HostedMachineCount,
			applicationCount: status[0].ServiceCount,
		}
	}
}
Example #20
0
// waitForAgentInitialisation polls the bootstrapped controller with a read-only
// command which will fail until the controller is fully initialised.
// TODO(wallyworld) - add a bespoke command to maybe the admin facade for this purpose.
func (c *bootstrapCommand) waitForAgentInitialisation(ctx *cmd.Context) error {
	attempts := utils.AttemptStrategy{
		Min:   bootstrapReadyPollCount,
		Delay: bootstrapReadyPollDelay,
	}
	var (
		apiAttempts int
		err         error
	)

	apiAttempts = 1
	for attempt := attempts.Start(); attempt.Next(); apiAttempts++ {
		err = c.tryAPI()
		if err == nil {
			ctx.Infof("Bootstrap complete, %s now available.", c.controllerName)
			break
		}
		// As the API server is coming up, it goes through a number of steps.
		// Initially the upgrade steps run, but the api server allows some
		// calls to be processed during the upgrade, but not the list blocks.
		// Logins are also blocked during space discovery.
		// It is also possible that the underlying database causes connections
		// to be dropped as it is initialising, or reconfiguring. These can
		// lead to EOF or "connection is shut down" error messages. We skip
		// these too, hoping that things come back up before the end of the
		// retry poll count.
		errorMessage := errors.Cause(err).Error()
		switch {
		case errors.Cause(err) == io.EOF,
			strings.HasSuffix(errorMessage, "connection is shut down"),
			strings.Contains(errorMessage, "spaces are still being discovered"):
			ctx.Infof("Waiting for API to become available")
			continue
		case params.ErrCode(err) == params.CodeUpgradeInProgress:
			ctx.Infof("Waiting for API to become available: %v", err)
			continue
		}
		break
	}
	return errors.Annotatef(err, "unable to contact api server after %d attempts", apiAttempts)
}
Example #21
0
// ServerError returns an error suitable for returning to an API
// client, with an error code suitable for various kinds of errors
// generated in packages outside the API.
func ServerError(err error) *params.Error {
	if err == nil {
		return nil
	}
	msg := err.Error()
	// Skip past annotations when looking for the code.
	err = errors.Cause(err)
	code, ok := singletonCode(err)
	switch {
	case ok:
	case errors.IsUnauthorized(err):
		code = params.CodeUnauthorized
	case errors.IsNotFound(err):
		code = params.CodeNotFound
	case errors.IsAlreadyExists(err):
		code = params.CodeAlreadyExists
	case errors.IsNotAssigned(err):
		code = params.CodeNotAssigned
	case state.IsHasAssignedUnitsError(err):
		code = params.CodeHasAssignedUnits
	case IsNoAddressSetError(err):
		code = params.CodeNoAddressSet
	case errors.IsNotProvisioned(err):
		code = params.CodeNotProvisioned
	case state.IsUpgradeInProgressError(err):
		code = params.CodeUpgradeInProgress
	case state.IsHasAttachmentsError(err):
		code = params.CodeMachineHasAttachedStorage
	case IsUnknownEnviromentError(err):
		code = params.CodeNotFound
	case errors.IsNotSupported(err):
		code = params.CodeNotSupported
	default:
		code = params.ErrCode(err)
	}
	return &params.Error{
		Message: msg,
		Code:    code,
	}
}