github.com/go-graphite/carbonapi@v0.17.0/zipper/helper/errors.go (about)

     1  package helper
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"net/http"
     7  	"net/url"
     8  	"strings"
     9  
    10  	"github.com/ansel1/merry"
    11  	"github.com/go-graphite/carbonapi/pkg/parser"
    12  	"github.com/go-graphite/carbonapi/zipper/types"
    13  )
    14  
    15  func requestError(err error, server string) merry.Error {
    16  	// with code InternalServerError by default, overwritten by custom error
    17  	if merry.Is(err, context.DeadlineExceeded) {
    18  		return types.ErrTimeoutExceeded.WithValue("server", server).WithCause(err)
    19  	}
    20  	if urlErr, ok := err.(*url.Error); ok {
    21  		if netErr, ok := urlErr.Err.(*net.OpError); ok {
    22  			return types.ErrBackendError.WithValue("server", server).WithCause(netErr)
    23  		}
    24  	}
    25  	if netErr, ok := err.(*net.OpError); ok {
    26  		return types.ErrBackendError.WithValue("server", server).WithCause(netErr)
    27  	}
    28  	return types.ErrResponceError.WithValue("server", server)
    29  }
    30  
    31  func HttpErrorCode(err merry.Error) (code int) {
    32  	if err == nil {
    33  		code = http.StatusOK
    34  	} else {
    35  		c := merry.RootCause(err)
    36  		if c == nil {
    37  			c = err
    38  		}
    39  
    40  		code = merry.HTTPCode(err)
    41  		if code == http.StatusNotFound {
    42  			return
    43  		} else if code == http.StatusInternalServerError && merry.Is(c, parser.ErrInvalidArg) {
    44  			// check for invalid args, see applyByNode rewrite function
    45  			code = http.StatusBadRequest
    46  		}
    47  
    48  		if code == http.StatusGatewayTimeout || code == http.StatusBadGateway || merry.Is(c, types.ErrFailedToFetch) {
    49  			// simplify code, one error type for communications errors, all we can retry
    50  			code = http.StatusServiceUnavailable
    51  		}
    52  	}
    53  
    54  	return
    55  }
    56  
    57  // for stable return code on multiply errors
    58  func recalcCode(code, newCode int) int {
    59  	if newCode == http.StatusGatewayTimeout || newCode == http.StatusBadGateway {
    60  		// simplify code, one error type for communications errors, all we can retry
    61  		newCode = http.StatusServiceUnavailable
    62  	}
    63  	if code == 0 || code == http.StatusNotFound {
    64  		return newCode
    65  	}
    66  
    67  	if newCode >= 400 && newCode < 500 && code >= 400 && code < 500 {
    68  		if newCode == http.StatusBadRequest {
    69  			return newCode
    70  		} else if newCode == http.StatusForbidden && code != http.StatusBadRequest {
    71  			return newCode
    72  		}
    73  	}
    74  	if newCode < code {
    75  		code = newCode
    76  	}
    77  	return code
    78  }
    79  
    80  // MerryRootError strip merry error chain
    81  func MerryRootError(err error) string {
    82  	c := merry.RootCause(err)
    83  	if c == nil {
    84  		c = err
    85  	}
    86  	return merryError(c)
    87  }
    88  
    89  func merryError(err error) string {
    90  	if msg := merry.Message(err); len(msg) > 0 {
    91  		return strings.TrimRight(msg, "\n")
    92  	} else {
    93  		return err.Error()
    94  	}
    95  }
    96  
    97  func MergeHttpErrors(errors []merry.Error) (int, []string) {
    98  	returnCode := http.StatusNotFound
    99  	errMsgs := make([]string, 0)
   100  	for _, err := range errors {
   101  		c := merry.RootCause(err)
   102  		if c == nil {
   103  			c = err
   104  		}
   105  
   106  		code := merry.HTTPCode(err)
   107  		if code == http.StatusNotFound {
   108  			continue
   109  		} else if code == http.StatusInternalServerError && merry.Is(c, parser.ErrInvalidArg) {
   110  			// check for invalid args, see applyByNode rewrite function
   111  			code = http.StatusBadRequest
   112  		}
   113  
   114  		errMsgs = append(errMsgs, merryError(c))
   115  
   116  		returnCode = recalcCode(returnCode, code)
   117  	}
   118  
   119  	return returnCode, errMsgs
   120  }
   121  
   122  func MergeHttpErrorMap(errorsMap map[string]merry.Error) (returnCode int, errMap map[string]string) {
   123  	returnCode = http.StatusNotFound
   124  	errMap = make(map[string]string)
   125  	for key, err := range errorsMap {
   126  		c := merry.RootCause(err)
   127  		if c == nil {
   128  			c = err
   129  		}
   130  
   131  		code := merry.HTTPCode(err)
   132  		if code == http.StatusNotFound {
   133  			continue
   134  		} else if code == http.StatusInternalServerError && merry.Is(c, parser.ErrInvalidArg) {
   135  			// check for invalid args, see applyByNode rewrite function
   136  			code = http.StatusBadRequest
   137  		}
   138  
   139  		msg := merryError(c)
   140  		errMap[key] = msg
   141  		returnCode = recalcCode(returnCode, code)
   142  	}
   143  
   144  	return
   145  }
   146  
   147  func HttpErrorByCode(err merry.Error) merry.Error {
   148  	var returnErr merry.Error
   149  	if err == nil {
   150  		returnErr = types.ErrNoMetricsFetched
   151  	} else {
   152  		code := merry.HTTPCode(err)
   153  		msg := stripHtmlTags(merry.Message(err), 0)
   154  		if code == http.StatusForbidden {
   155  			returnErr = types.ErrForbidden
   156  			if len(msg) > 0 {
   157  				// pass message to caller
   158  				returnErr = returnErr.WithMessage(msg)
   159  			}
   160  		} else if code == http.StatusServiceUnavailable || code == http.StatusBadGateway || code == http.StatusGatewayTimeout {
   161  			returnErr = types.ErrFailedToFetch.WithHTTPCode(code).WithMessage(msg)
   162  		} else {
   163  			returnErr = types.ErrFailed.WithHTTPCode(code).WithMessage(msg)
   164  		}
   165  	}
   166  
   167  	return returnErr
   168  }