diff --git a/api/server/runner_test.go b/api/server/runner_test.go index 0d3403876..24b559acd 100644 --- a/api/server/runner_test.go +++ b/api/server/runner_test.go @@ -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( []*models.App{ {Name: "myapp", Config: models.Config{}}, }, []*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: "/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: "/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: "/myroute", 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: "fnproject/fn-test-utils", Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: map[string][]string{"X-Function": {"Test"}}}, - {Path: "/mydne", AppName: "myapp", Image: "fnproject/imagethatdoesnotexist", Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30}, - {Path: "/mydnehot", AppName: "myapp", Image: "fnproject/imagethatdoesnotexist", Type: "sync", Format: "http", Memory: 128, Timeout: 30, IdleTimeout: 30}, - {Path: "/mydneregistry", AppName: "myapp", Image: "localhost:5000/fnproject/imagethatdoesnotexist", Type: "sync", Format: "http", Memory: 128, Timeout: 30, IdleTimeout: 30}, - {Path: "/myoom", AppName: "myapp", Image: "fnproject/fn-test-utils", Type: "sync", Memory: 8, Timeout: 30, IdleTimeout: 30}, + {Path: "/", AppName: "myapp", Image: rImg, Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg}, + {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: rImg, Type: "sync", Format: "json", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg}, + {Path: "/myroute", AppName: "myapp", Image: rImg, Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg}, + {Path: "/myerror", AppName: "myapp", Image: rImg, Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg}, + {Path: "/mydne", AppName: "myapp", Image: rImgBs1, Type: "sync", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg}, + {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: rImgBs2, Type: "sync", Format: "http", Memory: 128, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg}, + {Path: "/myoom", AppName: "myapp", Image: rImg, Type: "sync", Memory: 8, Timeout: 30, IdleTimeout: 30, Headers: rHdr, Config: rCfg}, }, nil, ) @@ -158,13 +164,12 @@ func TestRouteRunnerExecution(t *testing.T) { 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"}} - crasher := `{"sleepTime": 0, "isDebug": true, "isCrash": true}` // crash container - oomer := `{"sleepTime": 0, "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 - ok := `{"sleepTime": 0, "isDebug": true}` // good response / ok - respTypeLie := `{"responseContentType": "foo/bar", "sleepTime":0, "isDebug": true}` // Content-Type: foo/bar - respTypeJason := `{"jasonContentType": "foo/bar", "sleepTime":0, "isDebug": true}` // Content-Type: foo/bar + crasher := `{"isDebug": true, "isCrash": true}` // crash container + oomer := `{"isDebug": true, "allocateMemory": 12000000}` // ask for 12MB + badHot := `{"invalidResponse": true, "isDebug": true}` // write a not json/http as output + ok := `{"isDebug": true}` // good response / ok + respTypeLie := `{"responseContentType": "foo/bar", "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: multiLog := `{"sleepTime": 1, "isDebug": true}` @@ -181,7 +186,7 @@ func TestRouteRunnerExecution(t *testing.T) { }{ {"/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 {"/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", 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/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/myhot", multiLog, "GET", http.StatusOK, nil, "", multiLogExpect}, + {"/r/myapp/", multiLog, "GET", http.StatusOK, nil, "", multiLogExpect}, } { body := strings.NewReader(test.body) _, rec := routerRequest(t, srv.Router, test.method, test.path, body) diff --git a/images/fn-test-utils/fn-test-utils.go b/images/fn-test-utils/fn-test-utils.go index fb2b7b615..1c7aefb7a 100644 --- a/images/fn-test-utils/fn-test-utils.go +++ b/images/fn-test-utils/fn-test-utils.go @@ -191,6 +191,13 @@ func processRequest(ctx context.Context, in io.Reader) (*AppRequest, *AppRespons Leaks = append(Leaks, &chunk) } + if request.IsDebug { + info := getDockerInfo() + log.Printf("DockerInfo %+v", info) + data["DockerId"] = info.Id + data["DockerHostname"] = info.Hostname + } + // simulate crash if request.IsCrash { panic("Crash requested") @@ -216,8 +223,16 @@ func processRequest(ctx context.Context, in io.Reader) (*AppRequest, *AppRespons } func main() { + if os.Getenv("ENABLE_HEADER") != "" { + log.Printf("Container starting") + } + format, _ := os.LookupEnv("FN_FORMAT") 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) { @@ -412,3 +427,39 @@ func createFile(name string, size int) error { } 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 +}