mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
fn: user friendly timeout handling changes (#1021)
* fn: user friendly timeout handling changes Timeout setting in routes now means "maximum amount of time a function can run in a container". Total wait time for a given http request is now expected to be handled by the client. As long as the client waits, the LB, runner or agents will search for resources to schedule it.
This commit is contained in:
@@ -60,7 +60,7 @@ func TestGetExactCall(t *testing.T) {
|
||||
}
|
||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
||||
|
||||
callID := CallAsync(t, u, &bytes.Buffer{})
|
||||
callID := CallAsync(t, s.Context, u, &bytes.Buffer{})
|
||||
|
||||
cfg := &call.GetAppsAppCallsCallParams{
|
||||
Call: callID,
|
||||
|
||||
@@ -2,6 +2,7 @@ package tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/url"
|
||||
@@ -15,9 +16,9 @@ import (
|
||||
"github.com/fnproject/fn_go/models"
|
||||
)
|
||||
|
||||
func CallAsync(t *testing.T, u url.URL, content io.Reader) string {
|
||||
func CallAsync(t *testing.T, ctx context.Context, u url.URL, content io.Reader) string {
|
||||
output := &bytes.Buffer{}
|
||||
_, err := CallFN(u.String(), content, output, "POST", []string{})
|
||||
_, err := CallFN(ctx, u.String(), content, output, "POST", []string{})
|
||||
if err != nil {
|
||||
t.Errorf("Got unexpected error: %v", err)
|
||||
}
|
||||
@@ -41,9 +42,9 @@ func CallAsync(t *testing.T, u url.URL, content io.Reader) string {
|
||||
return callID.CallID
|
||||
}
|
||||
|
||||
func CallSync(t *testing.T, u url.URL, content io.Reader) string {
|
||||
func CallSync(t *testing.T, ctx context.Context, u url.URL, content io.Reader) string {
|
||||
output := &bytes.Buffer{}
|
||||
resp, err := CallFN(u.String(), content, output, "POST", []string{})
|
||||
resp, err := CallFN(ctx, u.String(), content, output, "POST", []string{})
|
||||
if err != nil {
|
||||
t.Errorf("Got unexpected error: %v", err)
|
||||
}
|
||||
@@ -75,7 +76,7 @@ func TestCanCallfunction(t *testing.T) {
|
||||
|
||||
content := &bytes.Buffer{}
|
||||
output := &bytes.Buffer{}
|
||||
_, err := CallFN(u.String(), content, output, "POST", []string{})
|
||||
_, err := CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
||||
if err != nil {
|
||||
t.Errorf("Got unexpected error: %v", err)
|
||||
}
|
||||
@@ -104,7 +105,7 @@ func TestCallOutputMatch(t *testing.T) {
|
||||
Name string
|
||||
}{Name: "John"})
|
||||
output := &bytes.Buffer{}
|
||||
_, err := CallFN(u.String(), content, output, "POST", []string{})
|
||||
_, err := CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
||||
if err != nil {
|
||||
t.Errorf("Got unexpected error: %v", err)
|
||||
}
|
||||
@@ -134,7 +135,7 @@ func TestCanCallAsync(t *testing.T) {
|
||||
Type: newRouteType,
|
||||
})
|
||||
|
||||
CallAsync(t, u, &bytes.Buffer{})
|
||||
CallAsync(t, s.Context, u, &bytes.Buffer{})
|
||||
}
|
||||
|
||||
func TestCanGetAsyncState(t *testing.T) {
|
||||
@@ -157,7 +158,7 @@ func TestCanGetAsyncState(t *testing.T) {
|
||||
Type: newRouteType,
|
||||
})
|
||||
|
||||
callID := CallAsync(t, u, &bytes.Buffer{})
|
||||
callID := CallAsync(t, s.Context, u, &bytes.Buffer{})
|
||||
cfg := &call.GetAppsAppCallsCallParams{
|
||||
Call: callID,
|
||||
App: s.AppName,
|
||||
@@ -221,7 +222,7 @@ func TestCanCauseTimeout(t *testing.T) {
|
||||
}{Seconds: 11})
|
||||
output := &bytes.Buffer{}
|
||||
|
||||
resp, _ := CallFN(u.String(), content, output, "POST", []string{})
|
||||
resp, _ := CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
||||
|
||||
if !strings.Contains(output.String(), "Timed out") {
|
||||
t.Errorf("Must fail because of timeout, but got error message: %v", output.String())
|
||||
@@ -270,7 +271,7 @@ func TestCallResponseHeadersMatch(t *testing.T) {
|
||||
u.Path = path.Join(u.Path, "r", s.AppName, rt.Path)
|
||||
content := &bytes.Buffer{}
|
||||
output := &bytes.Buffer{}
|
||||
CallFN(u.String(), content, output, "POST",
|
||||
CallFN(s.Context, u.String(), content, output, "POST",
|
||||
[]string{
|
||||
"ACCEPT: application/xml",
|
||||
"ACCEPT: application/json; q=0.2",
|
||||
@@ -305,7 +306,7 @@ func TestCanWriteLogs(t *testing.T) {
|
||||
Size int
|
||||
}{Size: 20})
|
||||
|
||||
callID := CallSync(t, u, content)
|
||||
callID := CallSync(t, s.Context, u, content)
|
||||
|
||||
cfg := &operations.GetAppsAppCallsCallLogParams{
|
||||
Call: callID,
|
||||
@@ -353,7 +354,7 @@ func TestOversizedLog(t *testing.T) {
|
||||
Size int
|
||||
}{Size: size}) //exceeding log by 1 symbol
|
||||
|
||||
callID := CallSync(t, u, content)
|
||||
callID := CallSync(t, s.Context, u, content)
|
||||
|
||||
cfg := &operations.GetAppsAppCallsCallLogParams{
|
||||
Call: callID,
|
||||
|
||||
@@ -41,7 +41,7 @@ func TestFnJSONFormats(t *testing.T) {
|
||||
})
|
||||
content := bytes.NewBuffer(b)
|
||||
output := &bytes.Buffer{}
|
||||
resp, err := CallFN(u.String(), content, output, "POST", []string{})
|
||||
resp, err := CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
||||
if err != nil {
|
||||
t.Errorf("Got unexpected error: %v", err)
|
||||
}
|
||||
|
||||
@@ -136,6 +136,8 @@ func (s *TestHarness) Cleanup() {
|
||||
for app, _ := range s.createdApps {
|
||||
safeDeleteApp(ctx, s.Client, app)
|
||||
}
|
||||
|
||||
s.Cancel()
|
||||
}
|
||||
|
||||
func EnvAsHeader(req *http.Request, selectedEnv []string) {
|
||||
@@ -151,7 +153,7 @@ func EnvAsHeader(req *http.Request, selectedEnv []string) {
|
||||
}
|
||||
}
|
||||
|
||||
func CallFN(u string, content io.Reader, output io.Writer, method string, env []string) (*http.Response, error) {
|
||||
func CallFN(ctx context.Context, u string, content io.Reader, output io.Writer, method string, env []string) (*http.Response, error) {
|
||||
if method == "" {
|
||||
if content == nil {
|
||||
method = "GET"
|
||||
@@ -164,8 +166,8 @@ func CallFN(u string, content io.Reader, output io.Writer, method string, env []
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error running route: %s", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
if len(env) > 0 {
|
||||
EnvAsHeader(req, env)
|
||||
|
||||
@@ -2,6 +2,7 @@ package tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -10,8 +11,8 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
apimodels "github.com/fnproject/fn/api/models"
|
||||
apiutils "github.com/fnproject/fn/test/fn-api-tests"
|
||||
sdkmodels "github.com/fnproject/fn_go/models"
|
||||
)
|
||||
@@ -75,7 +76,7 @@ func TestCanExecuteFunction(t *testing.T) {
|
||||
content := bytes.NewBuffer([]byte(body))
|
||||
output := &bytes.Buffer{}
|
||||
|
||||
resp, err := apiutils.CallFN(u.String(), content, output, "POST", []string{})
|
||||
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
||||
if err != nil {
|
||||
t.Errorf("Got unexpected error: %v", err)
|
||||
}
|
||||
@@ -118,7 +119,7 @@ func TestCanExecuteBigOutput(t *testing.T) {
|
||||
content := bytes.NewBuffer([]byte(body))
|
||||
output := &bytes.Buffer{}
|
||||
|
||||
resp, err := apiutils.CallFN(u.String(), content, output, "POST", []string{})
|
||||
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
||||
if err != nil {
|
||||
t.Errorf("Got unexpected error: %v", err)
|
||||
}
|
||||
@@ -163,7 +164,7 @@ func TestCanExecuteTooBigOutput(t *testing.T) {
|
||||
content := bytes.NewBuffer([]byte(body))
|
||||
output := &bytes.Buffer{}
|
||||
|
||||
resp, err := apiutils.CallFN(u.String(), content, output, "POST", []string{})
|
||||
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
||||
if err != nil {
|
||||
t.Errorf("Got unexpected error: %v", err)
|
||||
}
|
||||
@@ -208,7 +209,7 @@ func TestCanExecuteEmptyOutput(t *testing.T) {
|
||||
content := bytes.NewBuffer([]byte(body))
|
||||
output := &bytes.Buffer{}
|
||||
|
||||
resp, err := apiutils.CallFN(u.String(), content, output, "POST", []string{})
|
||||
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
||||
if err != nil {
|
||||
t.Errorf("Got unexpected error: %v", err)
|
||||
}
|
||||
@@ -256,7 +257,7 @@ func TestBasicConcurrentExecution(t *testing.T) {
|
||||
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true}`
|
||||
content := bytes.NewBuffer([]byte(body))
|
||||
output := &bytes.Buffer{}
|
||||
resp, err := apiutils.CallFN(u.String(), content, output, "POST", []string{})
|
||||
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
||||
if err != nil {
|
||||
results <- fmt.Errorf("Got unexpected error: %v", err)
|
||||
return
|
||||
@@ -288,10 +289,14 @@ func TestSaturatedSystem(t *testing.T) {
|
||||
|
||||
s := apiutils.SetupHarness()
|
||||
|
||||
// override default 60 secs with shorter.
|
||||
s.Cancel()
|
||||
s.Context, s.Cancel = context.WithTimeout(context.Background(), 4*time.Second)
|
||||
|
||||
s.GivenAppExists(t, &sdkmodels.App{Name: s.AppName})
|
||||
defer s.Cleanup()
|
||||
|
||||
timeout := int32(5)
|
||||
timeout := int32(1)
|
||||
|
||||
rt := s.BasicRoute()
|
||||
rt.Image = "fnproject/fn-test-utils"
|
||||
@@ -316,28 +321,8 @@ func TestSaturatedSystem(t *testing.T) {
|
||||
content := bytes.NewBuffer([]byte(body))
|
||||
output := &bytes.Buffer{}
|
||||
|
||||
resp, err := apiutils.CallFN(u.String(), content, output, "POST", []string{})
|
||||
if err != nil {
|
||||
if err != apimodels.ErrCallTimeoutServerBusy {
|
||||
t.Errorf("Got unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// LB may respond either with:
|
||||
// timeout: a timeout during a call to a runner
|
||||
// too busy: a timeout during LB retry loop
|
||||
exp1 := "{\"error\":{\"message\":\"Timed out - server too busy\"}}\n"
|
||||
exp2 := "{\"error\":{\"message\":\"Timed out\"}}\n"
|
||||
|
||||
actual := output.String()
|
||||
|
||||
if strings.Contains(exp1, actual) && len(exp1) == len(actual) {
|
||||
} else if strings.Contains(exp2, actual) && len(exp2) == len(actual) {
|
||||
} else {
|
||||
t.Errorf("Assertion error.\n\tExpected: %v or %v\n\tActual: %v", exp1, exp2, output.String())
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusServiceUnavailable && resp.StatusCode != http.StatusGatewayTimeout {
|
||||
t.Fatalf("StatusCode check failed on %v", resp.StatusCode)
|
||||
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
||||
if resp != nil || err == nil || s.Context.Err() == nil {
|
||||
t.Fatalf("Expected response: %v err:%v", resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user