mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
228 lines
5.5 KiB
Go
228 lines
5.5 KiB
Go
package models
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"net/url"
|
|
"path"
|
|
"strings"
|
|
|
|
apiErrors "github.com/go-openapi/errors"
|
|
)
|
|
|
|
const (
|
|
defaultRouteTimeout = 30 // seconds
|
|
htfnScaleDownTimeout = 30 // seconds
|
|
)
|
|
|
|
var (
|
|
ErrInvalidPayload = errors.New("Invalid payload")
|
|
ErrRoutesAlreadyExists = errors.New("Route already exists")
|
|
ErrRoutesCreate = errors.New("Could not create route")
|
|
ErrRoutesGet = errors.New("Could not get route from datastore")
|
|
ErrRoutesList = errors.New("Could not list routes from datastore")
|
|
ErrRoutesMissingNew = errors.New("Missing new route")
|
|
ErrRoutesNotFound = errors.New("Route not found")
|
|
ErrRoutesPathImmutable = errors.New("Could not update route - path is immutable")
|
|
ErrRoutesRemoving = errors.New("Could not remove route from datastore")
|
|
ErrRoutesUpdate = errors.New("Could not update route")
|
|
)
|
|
|
|
type Routes []*Route
|
|
|
|
type Route struct {
|
|
AppName string `json:"app_name"`
|
|
Path string `json:"path"`
|
|
Image string `json:"image"`
|
|
Memory uint64 `json:"memory"`
|
|
Headers http.Header `json:"headers"`
|
|
Type string `json:"type"`
|
|
Format string `json:"format"`
|
|
MaxConcurrency int `json:"max_concurrency"`
|
|
Timeout int32 `json:"timeout"`
|
|
IdleTimeout int32 `json:"idle_timeout"`
|
|
Config `json:"config"`
|
|
}
|
|
|
|
var (
|
|
ErrRoutesValidationFoundDynamicURL = errors.New("Dynamic URL is not allowed")
|
|
ErrRoutesValidationInvalidPath = errors.New("Invalid Path format")
|
|
ErrRoutesValidationInvalidType = errors.New("Invalid route Type")
|
|
ErrRoutesValidationInvalidFormat = errors.New("Invalid route Format")
|
|
ErrRoutesValidationMissingAppName = errors.New("Missing route AppName")
|
|
ErrRoutesValidationMissingImage = errors.New("Missing route Image")
|
|
ErrRoutesValidationMissingName = errors.New("Missing route Name")
|
|
ErrRoutesValidationMissingPath = errors.New("Missing route Path")
|
|
ErrRoutesValidationMissingType = errors.New("Missing route Type")
|
|
ErrRoutesValidationPathMalformed = errors.New("Path malformed")
|
|
ErrRoutesValidationNegativeTimeout = errors.New("Negative timeout")
|
|
ErrRoutesValidationNegativeIdleTimeout = errors.New("Negative idle timeout")
|
|
ErrRoutesValidationNegativeMaxConcurrency = errors.New("Negative MaxConcurrency")
|
|
)
|
|
|
|
// SetDefaults sets zeroed field to defaults.
|
|
func (r *Route) SetDefaults() {
|
|
if r.Memory == 0 {
|
|
r.Memory = 128
|
|
}
|
|
|
|
if r.Type == TypeNone {
|
|
r.Type = TypeSync
|
|
}
|
|
|
|
if r.Format == "" {
|
|
r.Format = FormatDefault
|
|
}
|
|
|
|
if r.MaxConcurrency == 0 {
|
|
r.MaxConcurrency = 1
|
|
}
|
|
|
|
if r.Headers == nil {
|
|
r.Headers = http.Header{}
|
|
}
|
|
|
|
if r.Config == nil {
|
|
r.Config = map[string]string{}
|
|
}
|
|
|
|
if r.Timeout == 0 {
|
|
r.Timeout = defaultRouteTimeout
|
|
}
|
|
|
|
//if r.IdleTimeout == 0 {
|
|
// r.IdleTimeout = htfnScaleDownTimeout
|
|
//}
|
|
}
|
|
|
|
// Validate validates field values, skipping zeroed fields if skipZero is true.
|
|
func (r *Route) Validate(skipZero bool) error {
|
|
var res []error
|
|
|
|
if !skipZero {
|
|
if r.AppName == "" {
|
|
res = append(res, ErrRoutesValidationMissingAppName)
|
|
}
|
|
|
|
if r.Image == "" {
|
|
res = append(res, ErrRoutesValidationMissingImage)
|
|
}
|
|
|
|
if r.Path == "" {
|
|
res = append(res, ErrRoutesValidationMissingPath)
|
|
}
|
|
}
|
|
|
|
if !skipZero || r.Path != "" {
|
|
u, err := url.Parse(r.Path)
|
|
if err != nil {
|
|
res = append(res, ErrRoutesValidationPathMalformed)
|
|
}
|
|
|
|
if strings.Contains(u.Path, ":") {
|
|
res = append(res, ErrRoutesValidationFoundDynamicURL)
|
|
}
|
|
|
|
if !path.IsAbs(u.Path) {
|
|
res = append(res, ErrRoutesValidationInvalidPath)
|
|
}
|
|
}
|
|
|
|
if !skipZero || r.Type != "" {
|
|
if r.Type != TypeAsync && r.Type != TypeSync {
|
|
res = append(res, ErrRoutesValidationInvalidType)
|
|
}
|
|
}
|
|
|
|
if !skipZero || r.Format != "" {
|
|
if r.Format != FormatDefault && r.Format != FormatHTTP {
|
|
res = append(res, ErrRoutesValidationInvalidFormat)
|
|
}
|
|
}
|
|
|
|
if r.MaxConcurrency < 0 {
|
|
res = append(res, ErrRoutesValidationNegativeMaxConcurrency)
|
|
}
|
|
|
|
if r.Timeout < 0 {
|
|
res = append(res, ErrRoutesValidationNegativeTimeout)
|
|
}
|
|
|
|
if r.IdleTimeout < 0 {
|
|
res = append(res, ErrRoutesValidationNegativeIdleTimeout)
|
|
}
|
|
|
|
if len(res) > 0 {
|
|
return apiErrors.CompositeValidationError(res...)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *Route) Clone() *Route {
|
|
var clone Route
|
|
clone.AppName = r.AppName
|
|
clone.Path = r.Path
|
|
clone.Update(r)
|
|
return &clone
|
|
}
|
|
|
|
// Update updates fields in r with non-zero field values from new.
|
|
// 0-length slice Header values, and empty-string Config values trigger removal of map entry.
|
|
func (r *Route) Update(new *Route) {
|
|
if new.Image != "" {
|
|
r.Image = new.Image
|
|
}
|
|
if new.Memory != 0 {
|
|
r.Memory = new.Memory
|
|
}
|
|
if new.Type != "" {
|
|
r.Type = new.Type
|
|
}
|
|
if new.Timeout != 0 {
|
|
r.Timeout = new.Timeout
|
|
}
|
|
if new.IdleTimeout != 0 {
|
|
r.IdleTimeout = new.IdleTimeout
|
|
}
|
|
if new.Format != "" {
|
|
r.Format = new.Format
|
|
}
|
|
if new.MaxConcurrency != 0 {
|
|
r.MaxConcurrency = new.MaxConcurrency
|
|
}
|
|
if new.Headers != nil {
|
|
if r.Headers == nil {
|
|
r.Headers = make(http.Header)
|
|
}
|
|
for k, v := range new.Headers {
|
|
if len(v) == 0 {
|
|
r.Headers.Del(k)
|
|
} else {
|
|
for _, val := range v {
|
|
r.Headers.Add(k, val)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if new.Config != nil {
|
|
if r.Config == nil {
|
|
r.Config = make(Config)
|
|
}
|
|
for k, v := range new.Config {
|
|
if v == "" {
|
|
delete(r.Config, k)
|
|
} else {
|
|
r.Config[k] = v
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//TODO are these sql LIKE queries? or strict matches?
|
|
type RouteFilter struct {
|
|
Path string
|
|
AppName string
|
|
Image string
|
|
}
|