mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Remove V1 endpoints and Routes (#1210)
Largely a removal job, however many tests, particularly system level ones relied on Routes. These have been migrated to use Fns. * Add 410 response to swagger * No app names in log tags * Adding constraint in GetCall for FnID * Adding test to check FnID is required on call * Add fn_id to call selector * Fix text in docker mem warning * Correct buildConfig func name * Test fix up * Removing CPU setting from Agent test CPU setting has been deprecated, but the code base is still riddled with it. This just removes it from this layer. Really we need to remove it from Call. * Remove fn id check on calls * Reintroduce fn id required on call * Adding fnID to calls for execute test * Correct setting of app id in middleware * Removes root middlewares ability to redirect fun invocations * Add over sized test check * Removing call fn id check
This commit is contained in:
committed by
Owen Cliffe
parent
6a01dae923
commit
d56a49b321
@@ -31,7 +31,7 @@ const (
|
||||
|
||||
var possibleStatuses = [...]string{"delayed", "queued", "running", "success", "error", "cancelled"}
|
||||
|
||||
// Call is a representation of a specific invocation of a route.
|
||||
// Call is a representation of a specific invocation of a fn.
|
||||
type Call struct {
|
||||
// Unique identifier representing a specific call.
|
||||
ID string `json:"id" db:"id"`
|
||||
@@ -73,9 +73,6 @@ type Call struct {
|
||||
// - client_request - Request was cancelled by a client.
|
||||
Status string `json:"status" db:"status"`
|
||||
|
||||
// Path of the route that is responsible for this call
|
||||
Path string `json:"path" db:"path"`
|
||||
|
||||
// Name of Docker image to use.
|
||||
Image string `json:"image,omitempty" db:"-"`
|
||||
|
||||
@@ -124,7 +121,7 @@ type Call struct {
|
||||
// Config is the set of configuration variables for the call
|
||||
Config Config `json:"config,omitempty" db:"-"`
|
||||
|
||||
// Annotations is the set of annotations for the app/route of the call.
|
||||
// Annotations is the set of annotations for the app/fn of the call.
|
||||
Annotations Annotations `json:"annotations,omitempty" db:"-"`
|
||||
|
||||
// Headers are headers from the request that created this call
|
||||
@@ -163,8 +160,6 @@ type Call struct {
|
||||
}
|
||||
|
||||
type CallFilter struct {
|
||||
Path string // match
|
||||
AppID string // match
|
||||
FnID string //match
|
||||
FromTime common.DateTime
|
||||
ToTime common.DateTime
|
||||
|
||||
@@ -33,29 +33,6 @@ type Datastore interface {
|
||||
// Returns ErrAppsNotFound if an App is not found.
|
||||
RemoveApp(ctx context.Context, appID string) error
|
||||
|
||||
// GetRoute looks up a matching Route for appName and the literal request route routePath.
|
||||
// Returns ErrDatastoreEmptyAppName when appName is empty, and ErrDatastoreEmptyRoutePath when
|
||||
// routePath is empty.
|
||||
// Returns ErrRoutesNotFound when no matching route is found.
|
||||
GetRoute(ctx context.Context, appID, routePath string) (*Route, error)
|
||||
|
||||
// GetRoutesByApp gets a slice of routes for a appName, optionally filtering on filter (filter.AppName is ignored).
|
||||
// Returns ErrDatastoreEmptyAppName if appName is empty.
|
||||
GetRoutesByApp(ctx context.Context, appID string, filter *RouteFilter) ([]*Route, error)
|
||||
|
||||
// InsertRoute inserts a route. Returns ErrDatastoreEmptyRoute when route is nil, and ErrDatastoreEmptyAppName
|
||||
// or ErrDatastoreEmptyRoutePath for empty AppName or Path.
|
||||
// Returns ErrRoutesAlreadyExists if the exact route.Path already exists
|
||||
InsertRoute(ctx context.Context, route *Route) (*Route, error)
|
||||
|
||||
// UpdateRoute updates route's Config and Header fields. Returns ErrDatastoreEmptyRoute when route is nil, and
|
||||
// ErrDatastoreEmptyAppName or ErrDatastoreEmptyRoutePath for empty AppName or Path.
|
||||
UpdateRoute(ctx context.Context, route *Route) (*Route, error)
|
||||
|
||||
// RemoveRoute removes a route. Returns ErrDatastoreEmptyAppID when appName is empty, and
|
||||
// ErrDatastoreEmptyRoutePath when routePath is empty. Returns ErrRoutesNotFound when no route exists.
|
||||
RemoveRoute(ctx context.Context, appID, routePath string) error
|
||||
|
||||
// InsertFn inserts a new function if one does not exist, applying any defaults necessary,
|
||||
InsertFn(ctx context.Context, fn *Fn) (*Fn, error)
|
||||
|
||||
|
||||
@@ -77,62 +77,10 @@ var (
|
||||
code: http.StatusBadRequest,
|
||||
error: errors.New("Invalid payload"),
|
||||
}
|
||||
ErrDatastoreEmptyRoute = err{
|
||||
code: http.StatusBadRequest,
|
||||
error: errors.New("Missing route"),
|
||||
}
|
||||
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"),
|
||||
}
|
||||
ErrRoutesInvalidImage = err{
|
||||
code: http.StatusBadRequest,
|
||||
error: errors.New("Invalid route Image"),
|
||||
}
|
||||
ErrRoutesMissingName = err{
|
||||
code: http.StatusBadRequest,
|
||||
error: errors.New("Missing route Name"),
|
||||
}
|
||||
ErrRoutesMissingPath = err{
|
||||
code: http.StatusBadRequest,
|
||||
error: errors.New("Missing route Path"),
|
||||
}
|
||||
ErrPathMalformed = err{
|
||||
code: http.StatusBadRequest,
|
||||
error: errors.New("Path malformed"),
|
||||
@@ -145,21 +93,9 @@ var (
|
||||
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),
|
||||
}
|
||||
ErrInvalidMemory = err{
|
||||
code: http.StatusBadRequest,
|
||||
error: fmt.Errorf("memory value is out of range. It should be between 0 and %d", RouteMaxMemory),
|
||||
error: fmt.Errorf("memory value is out of range. It should be between 0 and %d", MaxMemory),
|
||||
}
|
||||
ErrCallResourceTooBig = err{
|
||||
code: http.StatusBadRequest,
|
||||
@@ -178,14 +114,6 @@ var (
|
||||
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"),
|
||||
|
||||
@@ -18,6 +18,13 @@ var (
|
||||
MaxTimeout int32 = 300 // 5m
|
||||
MaxIdleTimeout int32 = 3600 // 1h
|
||||
|
||||
DefaultTimeout int32 = 30 // seconds
|
||||
DefaultIdleTimeout int32 = 30 // seconds
|
||||
DefaultMemory uint64 = 128 // MB
|
||||
|
||||
MaxSyncTimeout = 120 // 2 minutes
|
||||
MaxAsyncTimeout = 3600 // 1 hour
|
||||
|
||||
ErrFnsIDMismatch = err{
|
||||
code: http.StatusBadRequest,
|
||||
error: errors.New("Fn ID in path does not match that in body"),
|
||||
@@ -50,6 +57,10 @@ var (
|
||||
code: http.StatusBadRequest,
|
||||
error: errors.New("Missing image on Fn"),
|
||||
}
|
||||
ErrFnsInvalidImage = err{
|
||||
code: http.StatusBadRequest,
|
||||
error: errors.New("Invalid Fn image"),
|
||||
}
|
||||
ErrFnsInvalidFormat = err{
|
||||
code: http.StatusBadRequest,
|
||||
error: errors.New("Invalid format on Fn"),
|
||||
|
||||
@@ -16,7 +16,7 @@ type LogStore interface {
|
||||
|
||||
// TODO we should probably allow deletion of a range of logs (also calls)?
|
||||
// common cases for deletion will be:
|
||||
// * route gets nuked
|
||||
// * fn gets nuked
|
||||
// * app gets nuked
|
||||
// * call+logs getting cleaned up periodically
|
||||
|
||||
@@ -24,16 +24,9 @@ type LogStore interface {
|
||||
// exists.
|
||||
InsertCall(ctx context.Context, call *Call) error
|
||||
|
||||
// GetCall returns a call at a certain id and app name.
|
||||
GetCall1(ctx context.Context, appId, callID string) (*Call, error)
|
||||
|
||||
// GetCall2 returns a call at a certain id
|
||||
GetCall(ctx context.Context, fnID, callID string) (*Call, error)
|
||||
|
||||
// GetCalls returns a list of calls that satisfy the given CallFilter. If no
|
||||
// calls exist, an empty list and a nil error are returned.
|
||||
GetCalls1(ctx context.Context, filter *CallFilter) ([]*Call, error)
|
||||
|
||||
// GetCalls returns a list of calls that satisfy the given CallFilter. If no
|
||||
// calls exist, an empty list and a nil error are returned.
|
||||
GetCalls(ctx context.Context, filter *CallFilter) (*CallList, error)
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fnproject/fn/api/common"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultTimeout = 30 // seconds
|
||||
DefaultIdleTimeout = 30 // seconds
|
||||
DefaultMemory = 128 // MB
|
||||
|
||||
MaxSyncTimeout = 120 // 2 minutes
|
||||
MaxAsyncTimeout = 3600 // 1 hour
|
||||
)
|
||||
|
||||
var RouteMaxMemory = uint64(8 * 1024)
|
||||
|
||||
type Routes []*Route
|
||||
|
||||
type Route struct {
|
||||
AppID string `json:"app_id" db:"app_id"`
|
||||
Path string `json:"path" db:"path"`
|
||||
Image string `json:"image" db:"image"`
|
||||
Memory uint64 `json:"memory" db:"memory"`
|
||||
CPUs MilliCPUs `json:"cpus" db:"cpus"`
|
||||
Headers Headers `json:"headers,omitempty" db:"headers"`
|
||||
Type string `json:"type" db:"type"`
|
||||
Format string `json:"format" db:"format"`
|
||||
Timeout int32 `json:"timeout" db:"timeout"`
|
||||
IdleTimeout int32 `json:"idle_timeout" db:"idle_timeout"`
|
||||
TmpFsSize uint32 `json:"tmpfs_size" db:"tmpfs_size"`
|
||||
Config Config `json:"config,omitempty" db:"config"`
|
||||
Annotations Annotations `json:"annotations,omitempty" db:"annotations"`
|
||||
CreatedAt common.DateTime `json:"created_at,omitempty" db:"created_at"`
|
||||
UpdatedAt common.DateTime `json:"updated_at,omitempty" db:"updated_at"`
|
||||
}
|
||||
|
||||
// SetDefaults sets zeroed field to defaults.
|
||||
func (r *Route) SetDefaults() {
|
||||
if r.Memory == 0 {
|
||||
r.Memory = DefaultMemory
|
||||
}
|
||||
|
||||
if r.Type == TypeNone {
|
||||
r.Type = TypeSync
|
||||
}
|
||||
|
||||
if r.Format == "" {
|
||||
r.Format = FormatDefault
|
||||
}
|
||||
|
||||
if r.Headers == nil {
|
||||
r.Headers = Headers(http.Header{})
|
||||
}
|
||||
|
||||
if r.Config == nil {
|
||||
// keeps the json from being nil
|
||||
r.Config = map[string]string{}
|
||||
}
|
||||
|
||||
if r.Timeout == 0 {
|
||||
r.Timeout = DefaultTimeout
|
||||
}
|
||||
|
||||
if r.IdleTimeout == 0 {
|
||||
r.IdleTimeout = DefaultIdleTimeout
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Validate validates all field values, returning the first error, if any.
|
||||
func (r *Route) Validate() error {
|
||||
if r.AppID == "" {
|
||||
return ErrRoutesMissingAppID
|
||||
}
|
||||
|
||||
if r.Path == "" {
|
||||
return ErrRoutesMissingPath
|
||||
}
|
||||
|
||||
u, err := url.Parse(r.Path)
|
||||
if err != nil {
|
||||
return ErrPathMalformed
|
||||
}
|
||||
|
||||
if strings.Contains(u.Path, ":") {
|
||||
return ErrFoundDynamicURL
|
||||
}
|
||||
|
||||
if !path.IsAbs(u.Path) {
|
||||
return ErrRoutesInvalidPath
|
||||
}
|
||||
|
||||
if r.Image == "" {
|
||||
return ErrRoutesMissingImage
|
||||
}
|
||||
|
||||
if r.Type != TypeAsync && r.Type != TypeSync {
|
||||
return ErrRoutesInvalidType
|
||||
}
|
||||
|
||||
if r.Format != FormatDefault && r.Format != FormatHTTP && r.Format != FormatJSON && r.Format != FormatCloudEvent {
|
||||
return ErrRoutesInvalidFormat
|
||||
}
|
||||
|
||||
if r.Timeout <= 0 ||
|
||||
(r.Type == TypeSync && r.Timeout > MaxSyncTimeout) ||
|
||||
(r.Type == TypeAsync && r.Timeout > MaxAsyncTimeout) {
|
||||
return ErrRoutesInvalidTimeout
|
||||
}
|
||||
|
||||
if r.IdleTimeout <= 0 || r.IdleTimeout > MaxIdleTimeout {
|
||||
return ErrRoutesInvalidIdleTimeout
|
||||
}
|
||||
|
||||
if r.Memory < 1 || r.Memory > RouteMaxMemory {
|
||||
return ErrRoutesInvalidMemory
|
||||
}
|
||||
|
||||
err = r.Annotations.Validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Route) Clone() *Route {
|
||||
clone := new(Route)
|
||||
*clone = *r // shallow copy
|
||||
|
||||
// now deep copy the maps
|
||||
if r.Config != nil {
|
||||
clone.Config = make(Config, len(r.Config))
|
||||
for k, v := range r.Config {
|
||||
clone.Config[k] = v
|
||||
}
|
||||
}
|
||||
if r.Headers != nil {
|
||||
clone.Headers = make(Headers, len(r.Headers))
|
||||
for k, v := range r.Headers {
|
||||
// TODO technically, we need to deep copy this slice...
|
||||
clone.Headers[k] = v
|
||||
}
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
func (r1 *Route) Equals(r2 *Route) bool {
|
||||
// start off equal, check equivalence of each field.
|
||||
// the RHS of && won't eval if eq==false so config/headers checking is lazy
|
||||
|
||||
eq := true
|
||||
eq = eq && r1.AppID == r2.AppID
|
||||
eq = eq && r1.Path == r2.Path
|
||||
eq = eq && r1.Image == r2.Image
|
||||
eq = eq && r1.Memory == r2.Memory
|
||||
eq = eq && r1.CPUs == r2.CPUs
|
||||
eq = eq && r1.Headers.Equals(r2.Headers)
|
||||
eq = eq && r1.Type == r2.Type
|
||||
eq = eq && r1.Format == r2.Format
|
||||
eq = eq && r1.Timeout == r2.Timeout
|
||||
eq = eq && r1.IdleTimeout == r2.IdleTimeout
|
||||
eq = eq && r1.TmpFsSize == r2.TmpFsSize
|
||||
eq = eq && r1.Config.Equals(r2.Config)
|
||||
eq = eq && r1.Annotations.Equals(r2.Annotations)
|
||||
// NOTE: datastore tests are not very fun to write with timestamp checks,
|
||||
// and these are not values the user may set so we kind of don't care.
|
||||
//eq = eq && time.Time(r1.CreatedAt).Equal(time.Time(r2.CreatedAt))
|
||||
//eq = eq && time.Time(r2.UpdatedAt).Equal(time.Time(r2.UpdatedAt))
|
||||
return eq
|
||||
}
|
||||
|
||||
// Update updates fields in r with non-zero field values from new, and sets
|
||||
// updated_at if any of the fields change. 0-length slice Header values, and
|
||||
// empty-string Config values trigger removal of map entry.
|
||||
func (r *Route) Update(patch *Route) {
|
||||
original := r.Clone()
|
||||
|
||||
if patch.Image != "" {
|
||||
r.Image = patch.Image
|
||||
}
|
||||
if patch.Memory != 0 {
|
||||
r.Memory = patch.Memory
|
||||
}
|
||||
if patch.CPUs != 0 {
|
||||
r.CPUs = patch.CPUs
|
||||
}
|
||||
if patch.Type != "" {
|
||||
r.Type = patch.Type
|
||||
}
|
||||
if patch.Timeout != 0 {
|
||||
r.Timeout = patch.Timeout
|
||||
}
|
||||
if patch.IdleTimeout != 0 {
|
||||
r.IdleTimeout = patch.IdleTimeout
|
||||
}
|
||||
if patch.TmpFsSize != 0 {
|
||||
r.TmpFsSize = patch.TmpFsSize
|
||||
}
|
||||
if patch.Format != "" {
|
||||
r.Format = patch.Format
|
||||
}
|
||||
if patch.Headers != nil {
|
||||
if r.Headers == nil {
|
||||
r.Headers = Headers(make(http.Header))
|
||||
}
|
||||
for k, v := range patch.Headers {
|
||||
if len(v) == 0 {
|
||||
http.Header(r.Headers).Del(k)
|
||||
} else {
|
||||
r.Headers[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
if patch.Config != nil {
|
||||
if r.Config == nil {
|
||||
r.Config = make(Config)
|
||||
}
|
||||
for k, v := range patch.Config {
|
||||
if v == "" {
|
||||
delete(r.Config, k)
|
||||
} else {
|
||||
r.Config[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.Annotations = r.Annotations.MergeChange(patch.Annotations)
|
||||
|
||||
if !r.Equals(original) {
|
||||
r.UpdatedAt = common.DateTime(time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
type RouteFilter struct {
|
||||
PathPrefix string // this is prefix match TODO
|
||||
AppID string // this is exact match (important for security)
|
||||
Image string // this is exact match
|
||||
|
||||
Cursor string
|
||||
PerPage int
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/fnproject/fn/api/id"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRouteSimple(t *testing.T) {
|
||||
|
||||
route1 := &Route{
|
||||
AppID: id.New().String(),
|
||||
Path: "/some",
|
||||
Image: "foo",
|
||||
Memory: 128,
|
||||
CPUs: 100,
|
||||
Type: "sync",
|
||||
Format: "http",
|
||||
Timeout: 10,
|
||||
IdleTimeout: 10,
|
||||
TmpFsSize: 10,
|
||||
}
|
||||
|
||||
err := route1.Validate()
|
||||
if err != nil {
|
||||
t.Fatal("should not have failed, got: ", err)
|
||||
}
|
||||
|
||||
route2 := &Route{
|
||||
AppID: id.New().String(),
|
||||
Path: "/some",
|
||||
Image: "foo",
|
||||
Memory: 128,
|
||||
CPUs: 100,
|
||||
Type: "sync",
|
||||
Format: "nonsense",
|
||||
Timeout: 10,
|
||||
IdleTimeout: 10,
|
||||
}
|
||||
|
||||
err = route2.Validate()
|
||||
if err == nil {
|
||||
t.Fatalf("should have failed route: %#v", route2)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package models
|
||||
|
||||
type RouteWrapper struct {
|
||||
Route *Route `json:"route"`
|
||||
}
|
||||
|
||||
func (m *RouteWrapper) Validate() error {
|
||||
if m.Route != nil {
|
||||
return m.Route.Validate()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user