Files
fn-serverless/api/models/error.go
Tolga Ceylan eab85dfab0 fn: agent MaxRequestSize limit (#998)
* fn: agent MaxRequestSize limit

Currently, LimitRequestBody() exists to install a
http request body size in http/gin server. For production
enviroments, this is expected to be used. However, in agents
we may need to verify/enforce these size limits and to be
able to assert in case of missing limits is valuable.
With this change, operators can define an agent env variable
to limit this in addition to installing Gin/Http handler.

http.MaxBytesReader is superior in some cases as it sets
http headers (Connection: close) to guard against subsequent
requests.

However, NewClampReadCloser() is superior in other cases,
where it can cleanly return an API error for this case alone
(http.MaxBytesReader() does not return a clean error type
for overflow case, which makes it difficult to use it without
peeking into its implementation.)

For lb agent, upcoming changes rely on such limits enabled
and using gin/http handler (http.MaxBytesReader) makes such
checks/safety validations difficult.

* fn: read/write clamp code adjustment

In case of overflows, opt for simple implementation
of a partial write followed by return error.
2018-05-16 11:45:57 -07:00

251 lines
6.9 KiB
Go

package models
import (
"errors"
"fmt"
"net/http"
)
// TODO we can put constants all in this file too
const (
maxAppName = 30
)
var (
ErrInvalidJSON = err{
code: http.StatusBadRequest,
error: errors.New("Invalid JSON"),
}
ErrCallTimeout = err{
code: http.StatusGatewayTimeout,
error: errors.New("Timed out"),
}
ErrCallTimeoutServerBusy = err{
code: http.StatusServiceUnavailable,
error: errors.New("Timed out - server too busy"),
}
ErrAppsMissingName = err{
code: http.StatusBadRequest,
error: errors.New("Missing app name"),
}
ErrAppsTooLongName = err{
code: http.StatusBadRequest,
error: fmt.Errorf("App name must be %v characters or less", maxAppName),
}
ErrAppsInvalidName = err{
code: http.StatusBadRequest,
error: errors.New("Invalid app name"),
}
ErrAppsAlreadyExists = err{
code: http.StatusConflict,
error: errors.New("App already exists"),
}
ErrAppsMissingNew = err{
code: http.StatusBadRequest,
error: errors.New("Missing new application"),
}
ErrAppsNameImmutable = err{
code: http.StatusConflict,
error: errors.New("Could not update - name is immutable"),
}
ErrAppsNotFound = err{
code: http.StatusNotFound,
error: errors.New("App not found"),
}
ErrDeleteAppsWithRoutes = err{
code: http.StatusConflict,
error: errors.New("Cannot remove apps with routes"),
}
ErrDatastoreEmptyApp = err{
code: http.StatusBadRequest,
error: errors.New("Missing app"),
}
ErrDatastoreEmptyAppID = err{
code: http.StatusBadRequest,
error: errors.New("Missing app ID"),
}
ErrDatastoreEmptyRoute = err{
code: http.StatusBadRequest,
error: errors.New("Missing route"),
}
ErrDatastoreEmptyKey = err{
code: http.StatusBadRequest,
error: errors.New("Missing key"),
}
ErrDatastoreEmptyCallID = err{
code: http.StatusBadRequest,
error: errors.New("Missing call ID"),
}
ErrInvalidPayload = err{
code: http.StatusBadRequest,
error: errors.New("Invalid payload"),
}
ErrRoutesAlreadyExists = err{
code: http.StatusConflict,
error: errors.New("Route already exists"),
}
ErrRoutesMissingNew = err{
code: http.StatusBadRequest,
error: errors.New("Missing new route"),
}
ErrRoutesNotFound = err{
code: http.StatusNotFound,
error: errors.New("Route not found"),
}
ErrRoutesPathImmutable = err{
code: http.StatusConflict,
error: errors.New("Could not update - path is immutable"),
}
ErrFoundDynamicURL = err{
code: http.StatusBadRequest,
error: errors.New("Dynamic URL is not allowed"),
}
ErrRoutesInvalidPath = err{
code: http.StatusBadRequest,
error: errors.New("Invalid route path format"),
}
ErrRoutesInvalidType = err{
code: http.StatusBadRequest,
error: errors.New("Invalid route Type"),
}
ErrRoutesInvalidFormat = err{
code: http.StatusBadRequest,
error: errors.New("Invalid route Format"),
}
ErrRoutesMissingAppID = err{
code: http.StatusBadRequest,
error: errors.New("Missing route AppName"),
}
ErrRoutesMissingImage = err{
code: http.StatusBadRequest,
error: errors.New("Missing route Image"),
}
ErrRoutesMissingName = err{
code: http.StatusBadRequest,
error: errors.New("Missing route Name"),
}
ErrRoutesMissingPath = err{
code: http.StatusBadRequest,
error: errors.New("Missing route Path"),
}
ErrRoutesMissingType = err{
code: http.StatusBadRequest,
error: errors.New("Missing route Type"),
}
ErrPathMalformed = err{
code: http.StatusBadRequest,
error: errors.New("Path malformed"),
}
ErrInvalidToTime = err{
code: http.StatusBadRequest,
error: errors.New("to_time is not an epoch time"),
}
ErrInvalidFromTime = err{
code: http.StatusBadRequest,
error: errors.New("from_time is not an epoch time"),
}
ErrRoutesInvalidTimeout = err{
code: http.StatusBadRequest,
error: fmt.Errorf("timeout value is out of range. Sync should be between 0 and %d, async should be between 0 and %d", MaxSyncTimeout, MaxAsyncTimeout),
}
ErrRoutesInvalidIdleTimeout = err{
code: http.StatusBadRequest,
error: fmt.Errorf("idle_timeout value is out of range. It should be between 0 and %d", MaxIdleTimeout),
}
ErrRoutesInvalidMemory = err{
code: http.StatusBadRequest,
error: fmt.Errorf("memory value is out of range. It should be between 0 and %d", RouteMaxMemory),
}
ErrCallNotFound = err{
code: http.StatusNotFound,
error: errors.New("Call not found"),
}
ErrCallLogNotFound = err{
code: http.StatusNotFound,
error: errors.New("Call log not found"),
}
ErrInvokeNotSupported = err{
code: http.StatusBadRequest,
error: errors.New("Invoking routes /r/ is not supported on nodes configured as type API"),
}
ErrAPINotSupported = err{
code: http.StatusBadRequest,
error: errors.New("Invoking api /v1/ requests is not supported on nodes configured as type Runner"),
}
ErrPathNotFound = err{
code: http.StatusNotFound,
error: errors.New("Path not found"),
}
ErrInvalidCPUs = err{
code: http.StatusBadRequest,
error: fmt.Errorf("Cpus is invalid. Value should be either between [%.3f and %.3f] or [%dm and %dm] milliCPU units",
float64(MinMilliCPUs)/1000.0, float64(MaxMilliCPUs)/1000.0, MinMilliCPUs, MaxMilliCPUs),
}
ErrFunctionResponseTooBig = err{
code: http.StatusBadGateway,
error: fmt.Errorf("function response too large"),
}
ErrRequestContentTooBig = err{
code: http.StatusRequestEntityTooLarge,
error: fmt.Errorf("Request content too large"),
}
ErrInvalidAnnotationKey = err{
code: http.StatusBadRequest,
error: errors.New("Invalid annotation key, annotation keys must be non-empty ascii strings excluding whitespace"),
}
ErrInvalidAnnotationKeyLength = err{
code: http.StatusBadRequest,
error: fmt.Errorf("Invalid annotation key length, annotation keys may not be larger than %d bytes", maxAnnotationKeyBytes),
}
ErrInvalidAnnotationValue = err{
code: http.StatusBadRequest,
error: errors.New("Invalid annotation value, annotation values may only be non-empty strings, numbers, objects, or arrays"),
}
ErrInvalidAnnotationValueLength = err{
code: http.StatusBadRequest,
error: fmt.Errorf("Invalid annotation value length, annotation values may not be larger than %d bytes when serialized as JSON", maxAnnotationValueBytes),
}
ErrTooManyAnnotationKeys = err{
code: http.StatusBadRequest,
error: fmt.Errorf("Invalid annotation change, new key(s) exceed maximum permitted number of annotations keys (%d)", maxAnnotationsKeys),
}
)
// APIError any error that implements this interface will return an API response
// with the provided status code and error message body
type APIError interface {
Code() int
error
}
type err struct {
code int
error
}
func (e err) Code() int { return e.code }
func NewAPIError(code int, e error) APIError { return err{code, e} }
func IsAPIError(e error) bool {
_, ok := e.(APIError)
return ok
}
func GetAPIErrorCode(e error) int {
err, ok := e.(APIError)
if ok {
return err.Code()
}
return 0
}
// Error uniform error output
type Error struct {
Error *ErrorBody `json:"error,omitempty"`
}
func (m *Error) Validate() error {
return nil
}