[Feature] Function status

This commit is contained in:
Denis Makogon
2017-06-06 14:12:50 -07:00
parent 6334f44a72
commit 3f065ce6bf
29 changed files with 1165 additions and 564 deletions

View File

@@ -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
View 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
View 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})
}

View File

@@ -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) {

View File

@@ -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)

View File

@@ -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())

View File

@@ -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{}

View File

@@ -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 {

View File

@@ -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"`
}