github.com/juju/charmrepo/v7@v7.0.1/csclient/params/error.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the LGPLv3, see LICENCE file for details.
     3  
     4  package params // import "github.com/juju/charmrepo/v7/csclient/params"
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/go-macaroon-bakery/macaroon-bakery/v3/httpbakery"
    11  	"gopkg.in/errgo.v1"
    12  )
    13  
    14  // ErrorCode holds the class of an error in machine-readable format.
    15  // It is also an error in its own right.
    16  type ErrorCode string
    17  
    18  func (code ErrorCode) Error() string {
    19  	return string(code)
    20  }
    21  
    22  func (code ErrorCode) ErrorCode() ErrorCode {
    23  	return code
    24  }
    25  
    26  const (
    27  	// ErrOther is used as an error code when the
    28  	// charmstore returns an empty error code.
    29  	ErrOther            ErrorCode = "other charmstore error"
    30  	ErrNotFound         ErrorCode = "not found"
    31  	ErrMetadataNotFound ErrorCode = "metadata not found"
    32  	ErrForbidden        ErrorCode = "forbidden"
    33  	ErrBadRequest       ErrorCode = "bad request"
    34  	// TODO change to ErrAlreadyExists
    35  	ErrDuplicateUpload    ErrorCode = "duplicate upload"
    36  	ErrMultipleErrors     ErrorCode = "multiple errors"
    37  	ErrUnauthorized       ErrorCode = "unauthorized"
    38  	ErrMethodNotAllowed   ErrorCode = "method not allowed"
    39  	ErrServiceUnavailable ErrorCode = "service unavailable"
    40  	ErrEntityIdNotAllowed ErrorCode = "charm or bundle id not allowed"
    41  	ErrInvalidEntity      ErrorCode = "invalid charm or bundle"
    42  	ErrReadOnly           ErrorCode = "charmstore is in read-only mode"
    43  
    44  	// Note that these error codes sit in the same name space
    45  	// as the bakery error codes defined in gopkg.in/macaroon-bakery.v0/httpbakery .
    46  	// In particular, ErrBadRequest is a shared error code
    47  	// which needs to share the message too.
    48  )
    49  
    50  // Error represents an error - it is returned for any response that fails.
    51  // See https://github.com/juju/charmstore/blob/v4/docs/API.md#errors
    52  type Error struct {
    53  	Message string
    54  	Code    ErrorCode
    55  	Info    map[string]*Error `json:",omitempty"`
    56  }
    57  
    58  // NewError returns a new *Error with the given error code
    59  // and message.
    60  func NewError(code ErrorCode, f string, a ...interface{}) error {
    61  	return &Error{
    62  		Message: fmt.Sprintf(f, a...),
    63  		Code:    code,
    64  	}
    65  }
    66  
    67  // Error implements error.Error.
    68  func (e *Error) Error() string {
    69  	return e.Message
    70  }
    71  
    72  // ErrorCode holds the class of the error in
    73  // machine readable format.
    74  func (e *Error) ErrorCode() string {
    75  	return e.Code.Error()
    76  }
    77  
    78  // ErrorInfo returns additional info on the error.
    79  // TODO(rog) rename this so that it more accurately
    80  // reflects its role.
    81  func (e *Error) ErrorInfo() map[string]*Error {
    82  	return e.Info
    83  }
    84  
    85  // Cause implements errgo.Causer.Cause.
    86  func (e *Error) Cause() error {
    87  	if e.Code != "" {
    88  		return e.Code
    89  	}
    90  	return ErrOther
    91  }
    92  
    93  // TermAgreementRequiredError signals that the user
    94  // needs to agree to a set of terms and agreements
    95  // in order to complete an operation.
    96  type TermAgreementRequiredError struct {
    97  	Terms []string
    98  }
    99  
   100  // Error implements the error interface.
   101  func (e *TermAgreementRequiredError) Error() string {
   102  	return fmt.Sprintf("term agreement required %q", strings.Join(e.Terms, " "))
   103  }
   104  
   105  // MaybeTermsAgreementError returns err as a *TermAgreementRequiredError
   106  // if it has a "terms agreement required" error code, otherwise
   107  // it returns err unchanged.
   108  func MaybeTermsAgreementError(err error) error {
   109  	const code = "term agreement required"
   110  	e, ok := errgo.Cause(err).(*httpbakery.DischargeError)
   111  	if !ok || e.Reason == nil || e.Reason.Code != code {
   112  		return err
   113  	}
   114  	// Terms are guaranteed not to contain colon characters, so
   115  	// there's no ambiguity when finding where the listed terms start.
   116  	magicMarker := code + ":"
   117  	index := strings.LastIndex(e.Reason.Message, magicMarker)
   118  	if index == -1 {
   119  		return err
   120  	}
   121  	return &TermAgreementRequiredError{
   122  		Terms: strings.Fields(e.Reason.Message[index+len(magicMarker):]),
   123  	}
   124  }