mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
[Feature] Function status
This commit is contained in:
@@ -99,7 +99,7 @@ func TestAppDelete(t *testing.T) {
|
||||
{datastore.NewMockInit(
|
||||
[]*models.App{{
|
||||
Name: "myapp",
|
||||
}},nil,
|
||||
}},nil, nil,
|
||||
), "/v1/apps/myapp", "", http.StatusOK, nil},
|
||||
} {
|
||||
rnr, cancel := testRunner(t)
|
||||
@@ -219,14 +219,14 @@ func TestAppUpdate(t *testing.T) {
|
||||
{datastore.NewMockInit(
|
||||
[]*models.App{{
|
||||
Name: "myapp",
|
||||
}}, nil,
|
||||
}}, nil, nil,
|
||||
), "/v1/apps/myapp", `{ "app": { "config": { "test": "1" } } }`, http.StatusOK, nil},
|
||||
|
||||
// Addresses #380
|
||||
{datastore.NewMockInit(
|
||||
[]*models.App{{
|
||||
Name: "myapp",
|
||||
}}, nil,
|
||||
}}, nil,nil,
|
||||
), "/v1/apps/myapp", `{ "app": { "name": "othername" } }`, http.StatusBadRequest, nil},
|
||||
} {
|
||||
rnr, cancel := testRunner(t)
|
||||
|
||||
22
api/server/call_get.go
Normal file
22
api/server/call_get.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gitlab-odx.oracle.com/odx/functions/api"
|
||||
)
|
||||
|
||||
func (s *Server) handleCallGet(c *gin.Context) {
|
||||
ctx := c.MustGet("ctx").(context.Context)
|
||||
|
||||
callID := c.Param(api.Call)
|
||||
callObj, err := s.Datastore.GetTask(ctx, callID)
|
||||
if err != nil {
|
||||
handleErrorResponse(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, fnCallResponse{"Successfully loaded call", callObj})
|
||||
}
|
||||
35
api/server/call_list.go
Normal file
35
api/server/call_list.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gitlab-odx.oracle.com/odx/functions/api"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/models"
|
||||
)
|
||||
|
||||
func (s *Server) handleCallList(c *gin.Context) {
|
||||
ctx := c.MustGet("ctx").(context.Context)
|
||||
|
||||
appName, ok := c.MustGet(api.AppName).(string)
|
||||
if ok && appName == "" {
|
||||
c.JSON(http.StatusBadRequest, models.ErrRoutesValidationMissingAppName)
|
||||
return
|
||||
}
|
||||
appRoute, ok := c.MustGet(api.Path).(string)
|
||||
if ok && appRoute == "" {
|
||||
c.JSON(http.StatusBadRequest, models.ErrRoutesValidationMissingPath)
|
||||
return
|
||||
}
|
||||
|
||||
filter := models.CallFilter{AppName:appName, Path:appRoute}
|
||||
|
||||
calls, err := s.Datastore.GetTasks(ctx, &filter)
|
||||
if err != nil {
|
||||
handleErrorResponse(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, fnCallsResponse{"Successfully listed calls", calls})
|
||||
}
|
||||
@@ -20,6 +20,7 @@ var errStatusCode = map[error]int{
|
||||
models.ErrAppsAlreadyExists: http.StatusConflict,
|
||||
models.ErrRoutesNotFound: http.StatusNotFound,
|
||||
models.ErrRoutesAlreadyExists: http.StatusConflict,
|
||||
models.ErrCallNotFound: http.StatusNotFound,
|
||||
}
|
||||
|
||||
func handleErrorResponse(c *gin.Context, err error) {
|
||||
|
||||
@@ -80,7 +80,7 @@ func TestRouteDelete(t *testing.T) {
|
||||
{datastore.NewMockInit(nil,
|
||||
[]*models.Route{
|
||||
{Path: "/myroute", AppName: "a"},
|
||||
},
|
||||
}, nil,
|
||||
), "/v1/apps/a/routes/myroute", "", http.StatusOK, nil},
|
||||
} {
|
||||
rnr, cancel := testRunner(t)
|
||||
@@ -206,7 +206,7 @@ func TestRouteUpdate(t *testing.T) {
|
||||
AppName: "a",
|
||||
Path: "/myroute/do",
|
||||
},
|
||||
},
|
||||
}, nil,
|
||||
), "/v1/apps/a/routes/myroute/do", `{ "route": { "image": "funcy/hello" } }`, http.StatusOK, nil},
|
||||
|
||||
// Addresses #381
|
||||
@@ -216,7 +216,7 @@ func TestRouteUpdate(t *testing.T) {
|
||||
AppName: "a",
|
||||
Path: "/myroute/do",
|
||||
},
|
||||
},
|
||||
}, nil,
|
||||
), "/v1/apps/a/routes/myroute/do", `{ "route": { "path": "/otherpath" } }`, http.StatusBadRequest, nil},
|
||||
} {
|
||||
rnr, cancel := testRunner(t)
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-openapi/strfmt"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"gitlab-odx.oracle.com/odx/functions/api"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/models"
|
||||
@@ -151,7 +152,7 @@ func (s *Server) loadroutes(ctx context.Context, filter models.RouteFilter) ([]*
|
||||
}
|
||||
|
||||
// TODO: Should remove *gin.Context from these functions, should use only context.Context
|
||||
func (s *Server) serve(ctx context.Context, c *gin.Context, appName string, found *models.Route, app *models.App, route, reqID string, payload io.Reader, enqueue models.Enqueue) (ok bool) {
|
||||
func (s *Server) serve(ctx context.Context, c *gin.Context, appName string, found *models.Route, app *models.App, route, reqID string, payload io.Reader, enqueue models.Enqueue, ) (ok bool) {
|
||||
ctx, log := common.LoggerWithFields(ctx, logrus.Fields{"app": appName, "route": found.Path, "image": found.Image})
|
||||
|
||||
params, match := matchRoute(found.Path, route)
|
||||
@@ -215,6 +216,15 @@ func (s *Server) serve(ctx context.Context, c *gin.Context, appName string, foun
|
||||
}
|
||||
|
||||
s.Runner.Enqueue()
|
||||
createdAt := strfmt.DateTime(time.Now())
|
||||
newTask := &models.Task{}
|
||||
newTask.Image = &cfg.Image
|
||||
newTask.ID = cfg.ID
|
||||
newTask.CreatedAt = createdAt
|
||||
newTask.Path = found.Path
|
||||
newTask.EnvVars = cfg.Env
|
||||
newTask.AppName = cfg.AppName
|
||||
|
||||
switch found.Type {
|
||||
case "async":
|
||||
// Read payload
|
||||
@@ -224,24 +234,18 @@ func (s *Server) serve(ctx context.Context, c *gin.Context, appName string, foun
|
||||
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidPayload))
|
||||
return true
|
||||
}
|
||||
|
||||
// Create Task
|
||||
priority := int32(0)
|
||||
task := &models.Task{}
|
||||
task.Image = &cfg.Image
|
||||
task.ID = cfg.ID
|
||||
task.Path = found.Path
|
||||
task.AppName = cfg.AppName
|
||||
task.Priority = &priority
|
||||
task.EnvVars = cfg.Env
|
||||
task.Payload = string(pl)
|
||||
newTask.Priority = &priority
|
||||
newTask.Payload = string(pl)
|
||||
// Push to queue
|
||||
enqueue(c, s.MQ, task)
|
||||
enqueue(c, s.MQ, newTask)
|
||||
log.Info("Added new task to queue")
|
||||
c.JSON(http.StatusAccepted, map[string]string{"call_id": task.ID})
|
||||
c.JSON(http.StatusAccepted, map[string]string{"call_id": newTask.ID})
|
||||
|
||||
default:
|
||||
result, err := runner.RunTask(s.tasks, ctx, cfg)
|
||||
result, err := runner.RunTrackedTask(newTask, s.tasks, ctx, cfg, s.Datastore)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, runnerResponse{
|
||||
RequestID: cfg.ID,
|
||||
@@ -256,6 +260,10 @@ func (s *Server) serve(ctx context.Context, c *gin.Context, appName string, foun
|
||||
c.Header(k, v[0])
|
||||
}
|
||||
|
||||
// this will help users to track sync execution in a manner of async
|
||||
// FN_CALL_ID is an equivalent of call_id
|
||||
c.Header("FN_CALL_ID", newTask.ID)
|
||||
|
||||
switch result.Status() {
|
||||
case "success":
|
||||
c.Data(http.StatusOK, "", stdout.Bytes())
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestRouteRunnerAsyncExecution(t *testing.T) {
|
||||
{Type: "async", Path: "/myroute", AppName: "myapp", Image: "funcy/hello", Config: map[string]string{"test": "true"}},
|
||||
{Type: "async", Path: "/myerror", AppName: "myapp", Image: "funcy/error", Config: map[string]string{"test": "true"}},
|
||||
{Type: "async", Path: "/myroute/:param", AppName: "myapp", Image: "funcy/hello", Config: map[string]string{"test": "true"}},
|
||||
},
|
||||
}, nil,
|
||||
)
|
||||
mq := &mqs.Mock{}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ func TestRouteRunnerGet(t *testing.T) {
|
||||
srv := testServer(datastore.NewMockInit(
|
||||
[]*models.App{
|
||||
{Name: "myapp", Config: models.Config{}},
|
||||
}, nil,
|
||||
}, nil, nil,
|
||||
), &mqs.Mock{}, rnr, tasks)
|
||||
|
||||
for i, test := range []struct {
|
||||
@@ -76,7 +76,7 @@ func TestRouteRunnerPost(t *testing.T) {
|
||||
srv := testServer(datastore.NewMockInit(
|
||||
[]*models.App{
|
||||
{Name: "myapp", Config: models.Config{}},
|
||||
}, nil,
|
||||
}, nil, nil,
|
||||
), &mqs.Mock{}, rnr, tasks)
|
||||
|
||||
for i, test := range []struct {
|
||||
@@ -130,7 +130,7 @@ func TestRouteRunnerExecution(t *testing.T) {
|
||||
[]*models.Route{
|
||||
{Path: "/myroute", AppName: "myapp", Image: "funcy/hello", Headers: map[string][]string{"X-Function": {"Test"}}},
|
||||
{Path: "/myerror", AppName: "myapp", Image: "funcy/error", Headers: map[string][]string{"X-Function": {"Test"}}},
|
||||
},
|
||||
}, nil,
|
||||
), &mqs.Mock{}, rnr, tasks)
|
||||
|
||||
for i, test := range []struct {
|
||||
@@ -187,7 +187,7 @@ func TestRouteRunnerTimeout(t *testing.T) {
|
||||
},
|
||||
[]*models.Route{
|
||||
{Path: "/sleeper", AppName: "myapp", Image: "funcy/sleeper", Timeout: 1},
|
||||
},
|
||||
}, nil,
|
||||
), &mqs.Mock{}, rnr, tasks)
|
||||
|
||||
for i, test := range []struct {
|
||||
|
||||
@@ -245,7 +245,7 @@ func (s *Server) startGears(ctx context.Context) {
|
||||
})
|
||||
|
||||
svr.AddFunc(func(ctx context.Context) {
|
||||
runner.RunAsyncRunner(ctx, s.apiURL, s.tasks, s.Runner)
|
||||
runner.RunAsyncRunner(ctx, s.apiURL, s.tasks, s.Runner, s.Datastore)
|
||||
})
|
||||
|
||||
svr.AddFunc(func(ctx context.Context) {
|
||||
@@ -274,6 +274,8 @@ func (s *Server) bindHandlers(ctx context.Context) {
|
||||
|
||||
v1.GET("/routes", s.handleRouteList)
|
||||
|
||||
v1.GET("/calls/:call", s.handleCallGet)
|
||||
|
||||
apps := v1.Group("/apps/:app")
|
||||
{
|
||||
apps.GET("/routes", s.handleRouteList)
|
||||
@@ -281,6 +283,7 @@ func (s *Server) bindHandlers(ctx context.Context) {
|
||||
apps.GET("/routes/*route", s.handleRouteGet)
|
||||
apps.PATCH("/routes/*route", s.handleRouteUpdate)
|
||||
apps.DELETE("/routes/*route", s.handleRouteDelete)
|
||||
apps.GET("/calls/*route", s.handleCallList)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,3 +319,13 @@ type tasksResponse struct {
|
||||
Message string `json:"message"`
|
||||
Task models.Task `json:"tasksResponse"`
|
||||
}
|
||||
|
||||
type fnCallResponse struct {
|
||||
Message string `json:"message"`
|
||||
Call *models.FnCall `json:"call"`
|
||||
}
|
||||
|
||||
type fnCallsResponse struct {
|
||||
Message string `json:"message"`
|
||||
Calls models.FnCalls `json:"calls"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user