mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
fn: runner tests and test-utils enhancements (#807)
This is prep-work for more tests to come. *) remove http response -1, this will break in go 1.10 *) add docker id & hostname to fn-test-utils (will be useful to check/test which instance a request landed on.) *) add container start/stop logs in fn-test-utils. To detect if/how we miss logs during container start & end.
This commit is contained in:
@@ -133,20 +133,26 @@ func TestRouteRunnerExecution(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
rCfg := map[string]string{"ENABLE_HEADER": "yes", "ENABLE_FOOTER": "yes"} // enable container start/end header/footer
|
||||||
|
rHdr := map[string][]string{"X-Function": {"Test"}}
|
||||||
|
rImg := "fnproject/fn-test-utils"
|
||||||
|
rImgBs1 := "fnproject/imagethatdoesnotexist"
|
||||||
|
rImgBs2 := "localhost:5000/fnproject/imagethatdoesnotexist"
|
||||||
|
|
||||||
ds := datastore.NewMockInit(
|
ds := datastore.NewMockInit(
|
||||||
[]*models.App{
|
[]*models.App{
|
||||||
{Name: "myapp", Config: models.Config{}},
|
{Name: "myapp", Config: models.Config{}},
|
||||||
},
|
},
|
||||||
[]*models.Route{
|
[]*models.Route{
|
||||||
{Path: "/", AppName: "myapp", Image: "fnproject/fn-test-utils", Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: map[string][]string{"X-Function": {"Test"}}},
|
{Path: "/", AppName: "myapp", Image: rImg, Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg},
|
||||||
{Path: "/myhot", AppName: "myapp", Image: "fnproject/fn-test-utils", Type: "sync", Format: "http", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: map[string][]string{"X-Function": {"Test"}}},
|
{Path: "/myhot", AppName: "myapp", Image: rImg, Type: "sync", Format: "http", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg},
|
||||||
{Path: "/myhotjason", AppName: "myapp", Image: "fnproject/fn-test-utils", Type: "sync", Format: "json", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: map[string][]string{"X-Function": {"Test"}}},
|
{Path: "/myhotjason", AppName: "myapp", Image: rImg, Type: "sync", Format: "json", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg},
|
||||||
{Path: "/myroute", AppName: "myapp", Image: "fnproject/fn-test-utils", Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: map[string][]string{"X-Function": {"Test"}}},
|
{Path: "/myroute", AppName: "myapp", Image: rImg, Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg},
|
||||||
{Path: "/myerror", AppName: "myapp", Image: "fnproject/fn-test-utils", Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: map[string][]string{"X-Function": {"Test"}}},
|
{Path: "/myerror", AppName: "myapp", Image: rImg, Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg},
|
||||||
{Path: "/mydne", AppName: "myapp", Image: "fnproject/imagethatdoesnotexist", Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30},
|
{Path: "/mydne", AppName: "myapp", Image: rImgBs1, Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg},
|
||||||
{Path: "/mydnehot", AppName: "myapp", Image: "fnproject/imagethatdoesnotexist", Type: "sync", Format: "http", Memory: 128, Timeout: 30, IdleTimeout: 30},
|
{Path: "/mydnehot", AppName: "myapp", Image: rImgBs1, Type: "sync", Format: "http", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg},
|
||||||
{Path: "/mydneregistry", AppName: "myapp", Image: "localhost:5000/fnproject/imagethatdoesnotexist", Type: "sync", Format: "http", Memory: 128, Timeout: 30, IdleTimeout: 30},
|
{Path: "/mydneregistry", AppName: "myapp", Image: rImgBs2, Type: "sync", Format: "http", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg},
|
||||||
{Path: "/myoom", AppName: "myapp", Image: "fnproject/fn-test-utils", Type: "sync", Memory: 8, Timeout: 30, IdleTimeout: 30},
|
{Path: "/myoom", AppName: "myapp", Image: rImg, Type: "sync", Memory: 8, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg},
|
||||||
}, nil,
|
}, nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -158,13 +164,12 @@ func TestRouteRunnerExecution(t *testing.T) {
|
|||||||
expHeaders := map[string][]string{"X-Function": {"Test"}, "Content-Type": {"application/json; charset=utf-8"}}
|
expHeaders := map[string][]string{"X-Function": {"Test"}, "Content-Type": {"application/json; charset=utf-8"}}
|
||||||
expCTHeaders := map[string][]string{"X-Function": {"Test"}, "Content-Type": {"foo/bar"}}
|
expCTHeaders := map[string][]string{"X-Function": {"Test"}, "Content-Type": {"foo/bar"}}
|
||||||
|
|
||||||
crasher := `{"sleepTime": 0, "isDebug": true, "isCrash": true}` // crash container
|
crasher := `{"isDebug": true, "isCrash": true}` // crash container
|
||||||
oomer := `{"sleepTime": 0, "isDebug": true, "allocateMemory": 12000000}` // ask for 12MB
|
oomer := `{"isDebug": true, "allocateMemory": 12000000}` // ask for 12MB
|
||||||
badHttp := `{"sleepTime": 0, "isDebug": true, "responseCode": -1}` // http status of -1 (invalid http)
|
|
||||||
badHot := `{"invalidResponse": true, "isDebug": true}` // write a not json/http as output
|
badHot := `{"invalidResponse": true, "isDebug": true}` // write a not json/http as output
|
||||||
ok := `{"sleepTime": 0, "isDebug": true}` // good response / ok
|
ok := `{"isDebug": true}` // good response / ok
|
||||||
respTypeLie := `{"responseContentType": "foo/bar", "sleepTime":0, "isDebug": true}` // Content-Type: foo/bar
|
respTypeLie := `{"responseContentType": "foo/bar", "isDebug": true}` // Content-Type: foo/bar
|
||||||
respTypeJason := `{"jasonContentType": "foo/bar", "sleepTime":0, "isDebug": true}` // Content-Type: foo/bar
|
respTypeJason := `{"jasonContentType": "foo/bar", "isDebug": true}` // Content-Type: foo/bar
|
||||||
|
|
||||||
// sleep between logs and with debug enabled, fn-test-utils will log header/footer below:
|
// sleep between logs and with debug enabled, fn-test-utils will log header/footer below:
|
||||||
multiLog := `{"sleepTime": 1, "isDebug": true}`
|
multiLog := `{"sleepTime": 1, "isDebug": true}`
|
||||||
@@ -181,7 +186,7 @@ func TestRouteRunnerExecution(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{"/r/myapp/", ok, "GET", http.StatusOK, expHeaders, "", nil},
|
{"/r/myapp/", ok, "GET", http.StatusOK, expHeaders, "", nil},
|
||||||
|
|
||||||
{"/r/myapp/myhot", badHttp, "GET", http.StatusBadGateway, expHeaders, "invalid http response", nil},
|
{"/r/myapp/myhot", badHot, "GET", http.StatusBadGateway, expHeaders, "invalid http response", nil},
|
||||||
// hot container now back to normal, we should get OK
|
// hot container now back to normal, we should get OK
|
||||||
{"/r/myapp/myhot", ok, "GET", http.StatusOK, expHeaders, "", nil},
|
{"/r/myapp/myhot", ok, "GET", http.StatusOK, expHeaders, "", nil},
|
||||||
|
|
||||||
@@ -191,7 +196,6 @@ func TestRouteRunnerExecution(t *testing.T) {
|
|||||||
{"/r/myapp/myhotjason", respTypeLie, "GET", http.StatusOK, expCTHeaders, "", nil},
|
{"/r/myapp/myhotjason", respTypeLie, "GET", http.StatusOK, expCTHeaders, "", nil},
|
||||||
{"/r/myapp/myhotjason", respTypeJason, "GET", http.StatusOK, expCTHeaders, "", nil},
|
{"/r/myapp/myhotjason", respTypeJason, "GET", http.StatusOK, expCTHeaders, "", nil},
|
||||||
|
|
||||||
{"/r/myapp/myhot", badHot, "GET", http.StatusBadGateway, expHeaders, "invalid http response", nil},
|
|
||||||
{"/r/myapp/myhotjason", badHot, "GET", http.StatusBadGateway, expHeaders, "invalid json response", nil},
|
{"/r/myapp/myhotjason", badHot, "GET", http.StatusBadGateway, expHeaders, "invalid json response", nil},
|
||||||
|
|
||||||
{"/r/myapp/myroute", ok, "GET", http.StatusOK, expHeaders, "", nil},
|
{"/r/myapp/myroute", ok, "GET", http.StatusOK, expHeaders, "", nil},
|
||||||
@@ -203,6 +207,7 @@ func TestRouteRunnerExecution(t *testing.T) {
|
|||||||
|
|
||||||
{"/r/myapp/myoom", oomer, "GET", http.StatusBadGateway, nil, "container out of memory", nil},
|
{"/r/myapp/myoom", oomer, "GET", http.StatusBadGateway, nil, "container out of memory", nil},
|
||||||
{"/r/myapp/myhot", multiLog, "GET", http.StatusOK, nil, "", multiLogExpect},
|
{"/r/myapp/myhot", multiLog, "GET", http.StatusOK, nil, "", multiLogExpect},
|
||||||
|
{"/r/myapp/", multiLog, "GET", http.StatusOK, nil, "", multiLogExpect},
|
||||||
} {
|
} {
|
||||||
body := strings.NewReader(test.body)
|
body := strings.NewReader(test.body)
|
||||||
_, rec := routerRequest(t, srv.Router, test.method, test.path, body)
|
_, rec := routerRequest(t, srv.Router, test.method, test.path, body)
|
||||||
|
|||||||
@@ -191,6 +191,13 @@ func processRequest(ctx context.Context, in io.Reader) (*AppRequest, *AppRespons
|
|||||||
Leaks = append(Leaks, &chunk)
|
Leaks = append(Leaks, &chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if request.IsDebug {
|
||||||
|
info := getDockerInfo()
|
||||||
|
log.Printf("DockerInfo %+v", info)
|
||||||
|
data["DockerId"] = info.Id
|
||||||
|
data["DockerHostname"] = info.Hostname
|
||||||
|
}
|
||||||
|
|
||||||
// simulate crash
|
// simulate crash
|
||||||
if request.IsCrash {
|
if request.IsCrash {
|
||||||
panic("Crash requested")
|
panic("Crash requested")
|
||||||
@@ -216,8 +223,16 @@ func processRequest(ctx context.Context, in io.Reader) (*AppRequest, *AppRespons
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
if os.Getenv("ENABLE_HEADER") != "" {
|
||||||
|
log.Printf("Container starting")
|
||||||
|
}
|
||||||
|
|
||||||
format, _ := os.LookupEnv("FN_FORMAT")
|
format, _ := os.LookupEnv("FN_FORMAT")
|
||||||
testDo(format, os.Stdin, os.Stdout)
|
testDo(format, os.Stdin, os.Stdout)
|
||||||
|
|
||||||
|
if os.Getenv("ENABLE_FOOTER") != "" {
|
||||||
|
log.Printf("Container ending")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDo(format string, in io.Reader, out io.Writer) {
|
func testDo(format string, in io.Reader, out io.Writer) {
|
||||||
@@ -412,3 +427,39 @@ func createFile(name string, size int) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DockerInfo struct {
|
||||||
|
Hostname string
|
||||||
|
Id string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDockerInfo() DockerInfo {
|
||||||
|
var info DockerInfo
|
||||||
|
|
||||||
|
info.Hostname, _ = os.Hostname()
|
||||||
|
|
||||||
|
// cgroup file has lines such as, where last token is the docker id
|
||||||
|
/*
|
||||||
|
12:freezer:/docker/610d96c712c6983776f920f2bcf10fae056a6fe5274393c86678ca802d184b0a
|
||||||
|
*/
|
||||||
|
file, err := os.Open("/proc/self/cgroup")
|
||||||
|
if err == nil {
|
||||||
|
defer file.Close()
|
||||||
|
r := bufio.NewReader(file)
|
||||||
|
for {
|
||||||
|
line, _, err := r.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens := bytes.Split(line, []byte("/"))
|
||||||
|
tokLen := len(tokens)
|
||||||
|
if tokLen >= 3 && bytes.Compare(tokens[tokLen-2], []byte("docker")) == 0 {
|
||||||
|
info.Id = string(tokens[tokLen-1])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user