mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Merge branch 'func_logs2' into 'master'
Func logs feature See merge request !66
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
"gitlab-odx.oracle.com/odx/functions/api/datastore"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/models"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/mqs"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/logs"
|
||||
)
|
||||
|
||||
func setLogBuffer() *bytes.Buffer {
|
||||
@@ -28,25 +29,26 @@ func TestAppCreate(t *testing.T) {
|
||||
buf := setLogBuffer()
|
||||
for i, test := range []struct {
|
||||
mock models.Datastore
|
||||
logDB models.FnLog
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
// errors
|
||||
{datastore.NewMock(), "/v1/apps", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{datastore.NewMock(), "/v1/apps", `{}`, http.StatusBadRequest, models.ErrAppsMissingNew},
|
||||
{datastore.NewMock(), "/v1/apps", `{ "name": "Test" }`, http.StatusBadRequest, models.ErrAppsMissingNew},
|
||||
{datastore.NewMock(), "/v1/apps", `{ "app": { "name": "" } }`, http.StatusInternalServerError, models.ErrAppsValidationMissingName},
|
||||
{datastore.NewMock(), "/v1/apps", `{ "app": { "name": "1234567890123456789012345678901" } }`, http.StatusInternalServerError, models.ErrAppsValidationTooLongName},
|
||||
{datastore.NewMock(), "/v1/apps", `{ "app": { "name": "&&%@!#$#@$" } }`, http.StatusInternalServerError, models.ErrAppsValidationInvalidName},
|
||||
{datastore.NewMock(), "/v1/apps", `{ "app": { "name": "&&%@!#$#@$" } }`, http.StatusInternalServerError, models.ErrAppsValidationInvalidName},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps", `{}`, http.StatusBadRequest, models.ErrAppsMissingNew},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps", `{ "name": "Test" }`, http.StatusBadRequest, models.ErrAppsMissingNew},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps", `{ "app": { "name": "" } }`, http.StatusInternalServerError, models.ErrAppsValidationMissingName},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps", `{ "app": { "name": "1234567890123456789012345678901" } }`, http.StatusInternalServerError, models.ErrAppsValidationTooLongName},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps", `{ "app": { "name": "&&%@!#$#@$" } }`, http.StatusInternalServerError, models.ErrAppsValidationInvalidName},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps", `{ "app": { "name": "&&%@!#$#@$" } }`, http.StatusInternalServerError, models.ErrAppsValidationInvalidName},
|
||||
|
||||
// success
|
||||
{datastore.NewMock(), "/v1/apps", `{ "app": { "name": "teste" } }`, http.StatusOK, nil},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps", `{ "app": { "name": "teste" } }`, http.StatusOK, nil},
|
||||
} {
|
||||
rnr, cancel := testRunner(t)
|
||||
srv := testServer(test.mock, &mqs.Mock{}, rnr)
|
||||
srv := testServer(test.mock, &mqs.Mock{}, test.logDB, rnr)
|
||||
router := srv.Router
|
||||
|
||||
body := bytes.NewBuffer([]byte(test.body))
|
||||
@@ -76,20 +78,21 @@ func TestAppDelete(t *testing.T) {
|
||||
|
||||
for i, test := range []struct {
|
||||
ds models.Datastore
|
||||
logDB models.FnLog
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
{datastore.NewMock(), "/v1/apps/myapp", "", http.StatusNotFound, nil},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/myapp", "", http.StatusNotFound, nil},
|
||||
{datastore.NewMockInit(
|
||||
[]*models.App{{
|
||||
Name: "myapp",
|
||||
}}, nil, nil,
|
||||
), "/v1/apps/myapp", "", http.StatusOK, nil},
|
||||
}}, nil, nil, nil,
|
||||
), logs.NewMock(),"/v1/apps/myapp", "", http.StatusOK, nil},
|
||||
} {
|
||||
rnr, cancel := testRunner(t)
|
||||
srv := testServer(test.ds, &mqs.Mock{}, rnr)
|
||||
srv := testServer(test.ds, &mqs.Mock{}, test.logDB, rnr)
|
||||
|
||||
_, rec := routerRequest(t, srv.Router, "DELETE", test.path, nil)
|
||||
|
||||
@@ -117,7 +120,9 @@ func TestAppList(t *testing.T) {
|
||||
|
||||
rnr, cancel := testRunner(t)
|
||||
defer cancel()
|
||||
srv := testServer(datastore.NewMock(), &mqs.Mock{}, rnr)
|
||||
ds := datastore.NewMock()
|
||||
fnl := logs.NewMock()
|
||||
srv := testServer(ds, &mqs.Mock{}, fnl, rnr)
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
@@ -152,7 +157,9 @@ func TestAppGet(t *testing.T) {
|
||||
|
||||
rnr, cancel := testRunner(t)
|
||||
defer cancel()
|
||||
srv := testServer(datastore.NewMock(), &mqs.Mock{}, rnr)
|
||||
ds := datastore.NewMock()
|
||||
fnl := logs.NewMock()
|
||||
srv := testServer(ds, &mqs.Mock{}, fnl, rnr)
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
@@ -187,30 +194,31 @@ func TestAppUpdate(t *testing.T) {
|
||||
|
||||
for i, test := range []struct {
|
||||
mock models.Datastore
|
||||
logDB models.FnLog
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
// errors
|
||||
{datastore.NewMock(), "/v1/apps/myapp", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/myapp", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
|
||||
// success
|
||||
{datastore.NewMockInit(
|
||||
[]*models.App{{
|
||||
Name: "myapp",
|
||||
}}, nil, nil,
|
||||
), "/v1/apps/myapp", `{ "app": { "config": { "test": "1" } } }`, http.StatusOK, nil},
|
||||
}}, nil, nil, nil,
|
||||
), logs.NewMock(),"/v1/apps/myapp", `{ "app": { "config": { "test": "1" } } }`, http.StatusOK, nil},
|
||||
|
||||
// Addresses #380
|
||||
{datastore.NewMockInit(
|
||||
[]*models.App{{
|
||||
Name: "myapp",
|
||||
}}, nil, nil,
|
||||
), "/v1/apps/myapp", `{ "app": { "name": "othername" } }`, http.StatusBadRequest, nil},
|
||||
}}, nil, nil, nil,
|
||||
), logs.NewMock(),"/v1/apps/myapp", `{ "app": { "name": "othername" } }`, http.StatusBadRequest, nil},
|
||||
} {
|
||||
rnr, cancel := testRunner(t)
|
||||
srv := testServer(test.mock, &mqs.Mock{}, rnr)
|
||||
srv := testServer(test.mock, &mqs.Mock{}, test.logDB, rnr)
|
||||
|
||||
body := bytes.NewBuffer([]byte(test.body))
|
||||
_, rec := routerRequest(t, srv.Router, "PATCH", test.path, body)
|
||||
|
||||
46
api/server/call_logs.go
Normal file
46
api/server/call_logs.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gitlab-odx.oracle.com/odx/functions/api"
|
||||
)
|
||||
|
||||
func (s *Server) handleCallLogGet(c *gin.Context) {
|
||||
ctx := c.MustGet("ctx").(context.Context)
|
||||
|
||||
callID := c.Param(api.Call)
|
||||
_, err := s.Datastore.GetTask(ctx, callID)
|
||||
if err != nil {
|
||||
handleErrorResponse(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
callObj, err := s.LogDB.GetLog(ctx, callID)
|
||||
if err != nil {
|
||||
handleErrorResponse(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, fnCallLogResponse{"Successfully loaded call", callObj})
|
||||
}
|
||||
|
||||
|
||||
func (s *Server) handleCallLogDelete(c *gin.Context) {
|
||||
ctx := c.MustGet("ctx").(context.Context)
|
||||
|
||||
callID := c.Param(api.Call)
|
||||
_, err := s.Datastore.GetTask(ctx, callID)
|
||||
if err != nil {
|
||||
handleErrorResponse(c, err)
|
||||
return
|
||||
}
|
||||
err = s.LogDB.DeleteLog(ctx, callID)
|
||||
if err != nil {
|
||||
handleErrorResponse(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusAccepted, gin.H{"message": "Log delete accepted"})
|
||||
}
|
||||
@@ -21,6 +21,7 @@ var errStatusCode = map[error]int{
|
||||
models.ErrRoutesNotFound: http.StatusNotFound,
|
||||
models.ErrRoutesAlreadyExists: http.StatusConflict,
|
||||
models.ErrCallNotFound: http.StatusNotFound,
|
||||
models.ErrCallLogNotFound: http.StatusNotFound,
|
||||
}
|
||||
|
||||
func handleErrorResponse(c *gin.Context, err error) {
|
||||
|
||||
@@ -24,6 +24,7 @@ func init() {
|
||||
viper.SetDefault(EnvLogLevel, "info")
|
||||
viper.SetDefault(EnvMQURL, fmt.Sprintf("bolt://%s/data/worker_mq.db", cwd))
|
||||
viper.SetDefault(EnvDBURL, fmt.Sprintf("bolt://%s/data/bolt.db?bucket=funcs", cwd))
|
||||
viper.SetDefault(EnvLOGDBURL, fmt.Sprintf("bolt://%s/data/log.db?bucket=funcs", cwd))
|
||||
viper.SetDefault(EnvPort, 8080)
|
||||
viper.SetDefault(EnvAPIURL, fmt.Sprintf("http://127.0.0.1:%d", viper.GetInt(EnvPort)))
|
||||
viper.AutomaticEnv() // picks up env vars automatically
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"gitlab-odx.oracle.com/odx/functions/api/datastore"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/models"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/mqs"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/logs"
|
||||
)
|
||||
|
||||
func TestRouteCreate(t *testing.T) {
|
||||
@@ -16,26 +17,27 @@ func TestRouteCreate(t *testing.T) {
|
||||
|
||||
for i, test := range []struct {
|
||||
mock models.Datastore
|
||||
logDB models.FnLog
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
// errors
|
||||
{datastore.NewMock(), "/v1/apps/a/routes", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{datastore.NewMock(), "/v1/apps/a/routes", `{ }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
|
||||
{datastore.NewMock(), "/v1/apps/a/routes", `{ "path": "/myroute" }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
|
||||
{datastore.NewMock(), "/v1/apps/a/routes", `{ "route": { } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingPath},
|
||||
{datastore.NewMock(), "/v1/apps/a/routes", `{ "route": { "path": "/myroute" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage},
|
||||
{datastore.NewMock(), "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingPath},
|
||||
{datastore.NewMock(), "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "myroute" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidPath},
|
||||
{datastore.NewMock(), "/v1/apps/$/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusInternalServerError, models.ErrAppsValidationInvalidName},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/a/routes", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/a/routes", `{ }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/a/routes", `{ "path": "/myroute" }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/a/routes", `{ "route": { } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingPath},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/a/routes", `{ "route": { "path": "/myroute" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/a/routes", `{ "route": { "image": "funcy/hello" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingPath},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "myroute" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidPath},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/$/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusInternalServerError, models.ErrAppsValidationInvalidName},
|
||||
|
||||
// success
|
||||
{datastore.NewMock(), "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusOK, nil},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusOK, nil},
|
||||
} {
|
||||
rnr, cancel := testRunner(t)
|
||||
srv := testServer(test.mock, &mqs.Mock{}, rnr)
|
||||
srv := testServer(test.mock, &mqs.Mock{}, test.logDB, rnr)
|
||||
|
||||
body := bytes.NewBuffer([]byte(test.body))
|
||||
_, rec := routerRequest(t, srv.Router, "POST", test.path, body)
|
||||
@@ -67,20 +69,21 @@ func TestRouteDelete(t *testing.T) {
|
||||
|
||||
for i, test := range []struct {
|
||||
ds models.Datastore
|
||||
logDB models.FnLog
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
{datastore.NewMock(), "/v1/apps/a/routes/missing", "", http.StatusNotFound, nil},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/a/routes/missing", "", http.StatusNotFound, nil},
|
||||
{datastore.NewMockInit(nil,
|
||||
[]*models.Route{
|
||||
{Path: "/myroute", AppName: "a"},
|
||||
}, nil,
|
||||
), "/v1/apps/a/routes/myroute", "", http.StatusOK, nil},
|
||||
}, nil, nil,
|
||||
), logs.NewMock(),"/v1/apps/a/routes/myroute", "", http.StatusOK, nil},
|
||||
} {
|
||||
rnr, cancel := testRunner(t)
|
||||
srv := testServer(test.ds, &mqs.Mock{}, rnr)
|
||||
srv := testServer(test.ds, &mqs.Mock{}, test.logDB, rnr)
|
||||
_, rec := routerRequest(t, srv.Router, "DELETE", test.path, nil)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
@@ -107,7 +110,11 @@ func TestRouteList(t *testing.T) {
|
||||
|
||||
rnr, cancel := testRunner(t)
|
||||
defer cancel()
|
||||
srv := testServer(datastore.NewMock(), &mqs.Mock{}, rnr)
|
||||
|
||||
ds := datastore.NewMock()
|
||||
fnl := logs.NewMock()
|
||||
|
||||
srv := testServer(ds, &mqs.Mock{}, fnl, rnr)
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
@@ -143,7 +150,10 @@ func TestRouteGet(t *testing.T) {
|
||||
rnr, cancel := testRunner(t)
|
||||
defer cancel()
|
||||
|
||||
srv := testServer(datastore.NewMock(), &mqs.Mock{}, rnr)
|
||||
ds := datastore.NewMock()
|
||||
fnl := logs.NewMock()
|
||||
|
||||
srv := testServer(ds, &mqs.Mock{}, fnl, rnr)
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
@@ -178,16 +188,17 @@ func TestRouteUpdate(t *testing.T) {
|
||||
|
||||
for i, test := range []struct {
|
||||
ds models.Datastore
|
||||
logDB models.FnLog
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
// errors
|
||||
{datastore.NewMock(), "/v1/apps/a/routes/myroute/do", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{datastore.NewMock(), "/v1/apps/a/routes/myroute/do", `{}`, http.StatusBadRequest, models.ErrRoutesMissingNew},
|
||||
{datastore.NewMock(), "/v1/apps/a/routes/myroute/do", `{ "route": { "type": "invalid-type" } }`, http.StatusBadRequest, nil},
|
||||
{datastore.NewMock(), "/v1/apps/a/routes/myroute/do", `{ "route": { "format": "invalid-format" } }`, http.StatusBadRequest, nil},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/a/routes/myroute/do", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/a/routes/myroute/do", `{}`, http.StatusBadRequest, models.ErrRoutesMissingNew},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/a/routes/myroute/do", `{ "route": { "type": "invalid-type" } }`, http.StatusBadRequest, nil},
|
||||
{datastore.NewMock(), logs.NewMock(),"/v1/apps/a/routes/myroute/do", `{ "route": { "format": "invalid-format" } }`, http.StatusBadRequest, nil},
|
||||
|
||||
// success
|
||||
{datastore.NewMockInit(nil,
|
||||
@@ -196,8 +207,8 @@ func TestRouteUpdate(t *testing.T) {
|
||||
AppName: "a",
|
||||
Path: "/myroute/do",
|
||||
},
|
||||
}, nil,
|
||||
), "/v1/apps/a/routes/myroute/do", `{ "route": { "image": "funcy/hello" } }`, http.StatusOK, nil},
|
||||
}, nil, nil,
|
||||
), logs.NewMock(),"/v1/apps/a/routes/myroute/do", `{ "route": { "image": "funcy/hello" } }`, http.StatusOK, nil},
|
||||
|
||||
// Addresses #381
|
||||
{datastore.NewMockInit(nil,
|
||||
@@ -206,11 +217,12 @@ func TestRouteUpdate(t *testing.T) {
|
||||
AppName: "a",
|
||||
Path: "/myroute/do",
|
||||
},
|
||||
}, nil,
|
||||
), "/v1/apps/a/routes/myroute/do", `{ "route": { "path": "/otherpath" } }`, http.StatusBadRequest, nil},
|
||||
}, nil, nil,
|
||||
), logs.NewMock(),"/v1/apps/a/routes/myroute/do", `{ "route": { "path": "/otherpath" } }`, http.StatusBadRequest, nil},
|
||||
} {
|
||||
rnr, cancel := testRunner(t)
|
||||
srv := testServer(test.ds, &mqs.Mock{}, rnr)
|
||||
|
||||
srv := testServer(test.ds, &mqs.Mock{}, test.logDB, rnr)
|
||||
|
||||
body := bytes.NewBuffer([]byte(test.body))
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ func (s *Server) serve(ctx context.Context, c *gin.Context, appName string, foun
|
||||
c.JSON(http.StatusAccepted, map[string]string{"call_id": newTask.ID})
|
||||
|
||||
default:
|
||||
result, err := s.Runner.RunTrackedTask(newTask, ctx, cfg, s.Datastore)
|
||||
result, err := s.Runner.RunTrackedTask(newTask, ctx, cfg)
|
||||
if result != nil {
|
||||
waitTime := result.StartTime().Sub(cfg.ReceivedTime)
|
||||
c.Header("XXX-FXLB-WAIT", waitTime.String())
|
||||
|
||||
@@ -45,7 +45,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,
|
||||
}, nil, nil,
|
||||
)
|
||||
mq := &mqs.Mock{}
|
||||
|
||||
|
||||
@@ -11,11 +11,14 @@ import (
|
||||
"gitlab-odx.oracle.com/odx/functions/api/models"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/mqs"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/runner"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/logs"
|
||||
)
|
||||
|
||||
func testRunner(t *testing.T) (*runner.Runner, context.CancelFunc) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
r, err := runner.New(ctx, runner.NewFuncLogger(), runner.NewMetricLogger())
|
||||
ds := datastore.NewMock()
|
||||
fnl := logs.NewMock()
|
||||
r, err := runner.New(ctx, runner.NewFuncLogger(fnl), runner.NewMetricLogger(), ds)
|
||||
if err != nil {
|
||||
t.Fatal("Test: failed to create new runner")
|
||||
}
|
||||
@@ -24,15 +27,15 @@ func testRunner(t *testing.T) (*runner.Runner, context.CancelFunc) {
|
||||
|
||||
func TestRouteRunnerGet(t *testing.T) {
|
||||
buf := setLogBuffer()
|
||||
|
||||
rnr, cancel := testRunner(t)
|
||||
defer cancel()
|
||||
|
||||
srv := testServer(datastore.NewMockInit(
|
||||
ds := datastore.NewMockInit(
|
||||
[]*models.App{
|
||||
{Name: "myapp", Config: models.Config{}},
|
||||
}, nil, nil,
|
||||
), &mqs.Mock{}, rnr)
|
||||
}, nil, nil, nil,
|
||||
)
|
||||
logDB := logs.NewMock()
|
||||
srv := testServer(ds, &mqs.Mock{}, logDB, rnr)
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
@@ -70,11 +73,13 @@ func TestRouteRunnerPost(t *testing.T) {
|
||||
rnr, cancel := testRunner(t)
|
||||
defer cancel()
|
||||
|
||||
srv := testServer(datastore.NewMockInit(
|
||||
ds := datastore.NewMockInit(
|
||||
[]*models.App{
|
||||
{Name: "myapp", Config: models.Config{}},
|
||||
}, nil, nil,
|
||||
), &mqs.Mock{}, rnr)
|
||||
}, nil, nil, nil,
|
||||
)
|
||||
fnl := logs.NewMock()
|
||||
srv := testServer(ds, &mqs.Mock{}, fnl, rnr)
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
@@ -114,15 +119,20 @@ func TestRouteRunnerExecution(t *testing.T) {
|
||||
rnr, cancelrnr := testRunner(t)
|
||||
defer cancelrnr()
|
||||
|
||||
srv := testServer(datastore.NewMockInit(
|
||||
ds := datastore.NewMockInit(
|
||||
[]*models.App{
|
||||
{Name: "myapp", Config: models.Config{}},
|
||||
},
|
||||
[]*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)
|
||||
}, nil, nil,
|
||||
)
|
||||
|
||||
|
||||
fnl := logs.NewMock()
|
||||
srv := testServer(ds, &mqs.Mock{}, fnl, rnr)
|
||||
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
@@ -167,15 +177,17 @@ func TestRouteRunnerTimeout(t *testing.T) {
|
||||
rnr, cancelrnr := testRunner(t)
|
||||
defer cancelrnr()
|
||||
|
||||
srv := testServer(datastore.NewMockInit(
|
||||
ds := datastore.NewMockInit(
|
||||
[]*models.App{
|
||||
{Name: "myapp", Config: models.Config{}},
|
||||
},
|
||||
[]*models.Route{
|
||||
{Path: "/sleeper", AppName: "myapp", Image: "funcy/sleeper", Timeout: 1},
|
||||
}, nil,
|
||||
), &mqs.Mock{}, rnr)
|
||||
|
||||
}, nil, nil,
|
||||
)
|
||||
fnl := logs.NewMock()
|
||||
srv := testServer(ds, &mqs.Mock{}, fnl, rnr)
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
|
||||
@@ -24,22 +24,25 @@ import (
|
||||
"gitlab-odx.oracle.com/odx/functions/api/runner"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/runner/common"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/server/internal/routecache"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/logs"
|
||||
)
|
||||
|
||||
const (
|
||||
EnvLogLevel = "log_level"
|
||||
EnvMQURL = "mq_url"
|
||||
EnvDBURL = "db_url"
|
||||
EnvLOGDBURL = "logstore_url"
|
||||
EnvPort = "port" // be careful, Gin expects this variable to be "port"
|
||||
EnvAPIURL = "api_url"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Datastore models.Datastore
|
||||
Runner *runner.Runner
|
||||
Router *gin.Engine
|
||||
MQ models.MessageQueue
|
||||
Enqueue models.Enqueue
|
||||
Datastore models.Datastore
|
||||
Runner *runner.Runner
|
||||
Router *gin.Engine
|
||||
MQ models.MessageQueue
|
||||
Enqueue models.Enqueue
|
||||
LogDB models.FnLog
|
||||
|
||||
apiURL string
|
||||
|
||||
@@ -67,17 +70,22 @@ func NewFromEnv(ctx context.Context) *Server {
|
||||
logrus.WithError(err).Fatal("Error initializing message queue.")
|
||||
}
|
||||
|
||||
logDB, err := logs.New(viper.GetString(EnvLOGDBURL))
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Error initializing logs store.")
|
||||
}
|
||||
|
||||
apiURL := viper.GetString(EnvAPIURL)
|
||||
|
||||
return New(ctx, ds, mq, apiURL)
|
||||
return New(ctx, ds, mq, logDB, apiURL)
|
||||
}
|
||||
|
||||
// New creates a new Functions server with the passed in datastore, message queue and API URL
|
||||
func New(ctx context.Context, ds models.Datastore, mq models.MessageQueue, apiURL string, opts ...ServerOption) *Server {
|
||||
func New(ctx context.Context, ds models.Datastore, mq models.MessageQueue, logDB models.FnLog, apiURL string, opts ...ServerOption) *Server {
|
||||
metricLogger := runner.NewMetricLogger()
|
||||
funcLogger := runner.NewFuncLogger()
|
||||
funcLogger := runner.NewFuncLogger(logDB)
|
||||
|
||||
rnr, err := runner.New(ctx, funcLogger, metricLogger)
|
||||
rnr, err := runner.New(ctx, funcLogger, metricLogger, ds)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatalln("Failed to create a runner")
|
||||
return nil
|
||||
@@ -89,6 +97,7 @@ func New(ctx context.Context, ds models.Datastore, mq models.MessageQueue, apiUR
|
||||
Datastore: ds,
|
||||
MQ: mq,
|
||||
hotroutes: routecache.New(cacheSize),
|
||||
LogDB: logDB,
|
||||
Enqueue: DefaultEnqueue,
|
||||
apiURL: apiURL,
|
||||
}
|
||||
@@ -302,6 +311,8 @@ func (s *Server) bindHandlers(ctx context.Context) {
|
||||
v1.GET("/routes", s.handleRouteList)
|
||||
|
||||
v1.GET("/calls/:call", s.handleCallGet)
|
||||
v1.GET("/calls/:call/log", s.handleCallLogGet)
|
||||
v1.DELETE("/calls/:call/log", s.handleCallLogDelete)
|
||||
|
||||
apps := v1.Group("/apps/:app")
|
||||
{
|
||||
@@ -356,3 +367,8 @@ type fnCallsResponse struct {
|
||||
Message string `json:"message"`
|
||||
Calls models.FnCalls `json:"calls"`
|
||||
}
|
||||
|
||||
type fnCallLogResponse struct {
|
||||
Message string `json:"message"`
|
||||
Log *models.FnCallLog `json:"log"`
|
||||
}
|
||||
|
||||
@@ -17,17 +17,21 @@ import (
|
||||
"gitlab-odx.oracle.com/odx/functions/api/mqs"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/runner"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/server/internal/routecache"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/logs"
|
||||
)
|
||||
|
||||
var tmpBolt = "/tmp/func_test_bolt.db"
|
||||
var tmpDatastoreBolt = "/tmp/func_test_bolt_datastore.db"
|
||||
var tmpLogBolt = "/tmp/func_test_bolt_log.db"
|
||||
|
||||
func testServer(ds models.Datastore, mq models.MessageQueue, rnr *runner.Runner) *Server {
|
||||
|
||||
func testServer(ds models.Datastore, mq models.MessageQueue, logDB models.FnLog, rnr *runner.Runner) *Server {
|
||||
ctx := context.Background()
|
||||
|
||||
s := &Server{
|
||||
Runner: rnr,
|
||||
Router: gin.New(),
|
||||
Datastore: ds,
|
||||
LogDB: nil,
|
||||
MQ: mq,
|
||||
Enqueue: DefaultEnqueue,
|
||||
hotroutes: routecache.New(2),
|
||||
@@ -79,26 +83,33 @@ func getErrorResponse(t *testing.T, rec *httptest.ResponseRecorder) models.Error
|
||||
return errResp
|
||||
}
|
||||
|
||||
func prepareBolt(t *testing.T) (models.Datastore, func()) {
|
||||
os.Remove(tmpBolt)
|
||||
ds, err := datastore.New("bolt://" + tmpBolt)
|
||||
func prepareBolt(ctx context.Context, t *testing.T) (models.Datastore, models.FnLog, func()) {
|
||||
os.Remove(tmpDatastoreBolt)
|
||||
os.Remove(tmpLogBolt)
|
||||
ds, err := datastore.New("bolt://" + tmpDatastoreBolt)
|
||||
if err != nil {
|
||||
t.Fatal("Error when creating datastore: %s", err)
|
||||
t.Fatalf("Error when creating datastore: %s", err)
|
||||
}
|
||||
return ds, func() {
|
||||
os.Remove(tmpBolt)
|
||||
logDB, err := logs.New("bolt://" + tmpLogBolt)
|
||||
if err != nil {
|
||||
t.Fatalf("Error when creating log store: %s", err)
|
||||
}
|
||||
return ds,logDB, func() {
|
||||
os.Remove(tmpDatastoreBolt)
|
||||
os.Remove(tmpLogBolt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFullStack(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
buf := setLogBuffer()
|
||||
ds, closeBolt := prepareBolt(t)
|
||||
ds, logDB, closeBolt := prepareBolt(ctx, t)
|
||||
defer closeBolt()
|
||||
|
||||
rnr, rnrcancel := testRunner(t)
|
||||
defer rnrcancel()
|
||||
|
||||
srv := testServer(ds, &mqs.Mock{}, rnr)
|
||||
srv := testServer(ds, &mqs.Mock{}, logDB, rnr)
|
||||
srv.hotroutes = routecache.New(2)
|
||||
|
||||
for _, test := range []struct {
|
||||
|
||||
Reference in New Issue
Block a user