diff --git a/images/fn-test-utils/.gitignore b/images/fn-test-utils/.gitignore new file mode 100644 index 000000000..22d0d82f8 --- /dev/null +++ b/images/fn-test-utils/.gitignore @@ -0,0 +1 @@ +vendor diff --git a/images/fn-test-utils/Dockerfile b/images/fn-test-utils/Dockerfile new file mode 100644 index 000000000..b05b6b80c --- /dev/null +++ b/images/fn-test-utils/Dockerfile @@ -0,0 +1,16 @@ + +# build stage +FROM golang:1.9-alpine AS build-env +RUN apk --no-cache add build-base git bzr mercurial gcc +ENV D=/go/src/github.com/fnproject/fn/images/fn-test-utils +RUN go get -u github.com/golang/dep/cmd/dep +ADD Gopkg.* $D/ +RUN cd $D && dep ensure --vendor-only +ADD . $D +RUN cd $D && go build -o fn-test-utils && cp fn-test-utils /tmp/ + +# final stage +FROM alpine +WORKDIR /function +COPY --from=build-env /tmp/fn-test-utils /function +ENTRYPOINT ["./fn-test-utils"] diff --git a/images/fn-test-utils/Gopkg.lock b/images/fn-test-utils/Gopkg.lock new file mode 100644 index 000000000..dcebfda2b --- /dev/null +++ b/images/fn-test-utils/Gopkg.lock @@ -0,0 +1,15 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/fnproject/fdk-go" + packages = ["."] + revision = "ce12b15e559bb56980c4134cbeadb99db9cd563a" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "c55f0d3da5ec2e9e5c9a7c563702e4cf28513fa1aaea1c18664ca2cb7d726f89" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/images/fn-test-utils/Gopkg.toml b/images/fn-test-utils/Gopkg.toml new file mode 100644 index 000000000..4af110c13 --- /dev/null +++ b/images/fn-test-utils/Gopkg.toml @@ -0,0 +1,25 @@ +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" + + +[[constraint]] + branch = "master" + name = "github.com/fnproject/fdk-go" diff --git a/images/fn-test-utils/build.sh b/images/fn-test-utils/build.sh new file mode 100755 index 000000000..66f91bee0 --- /dev/null +++ b/images/fn-test-utils/build.sh @@ -0,0 +1,2 @@ +set -e +docker build -t fnproject/fn-test-utils:latest . diff --git a/images/fn-test-utils/fn-test-utils.go b/images/fn-test-utils/fn-test-utils.go new file mode 100644 index 000000000..e27c17d58 --- /dev/null +++ b/images/fn-test-utils/fn-test-utils.go @@ -0,0 +1,92 @@ +package main + +import ( + "context" + "encoding/json" + "io" + "log" + "time" + + fdk "github.com/fnproject/fdk-go" + "net/http" +) + +type AppRequest struct { + // if specified we 'sleep' the specified msecs + SleepTime int `json:"sleepTime,omitempty"` + // if specified, this is our response http status code + ResponseCode int `json:"responseCode,omitempty"` + // if specified, this is our response content-type + ResponseContentType string `json:"responseContentType,omitempty"` + // if specified, this is echoed back to client + EchoContent string `json:"echoContent,omitempty"` + // verbose mode + IsDebug bool `json:"isDebug,omitempty"` + // simulate crash + IsCrash bool `json:"isCrash,omitempty"` + // TODO: simulate slow read/slow write + // TODO: simulate partial write/read + // TODO: simulate mem leak + // TODO: simulate high cpu usage + // TODO: simulate high mem usage + // TODO: simulate large body upload/download +} + +type AppResponse struct { + Request AppRequest `json:"request"` + Headers http.Header `json:"header"` + Config map[string]string `json:"config"` +} + +func AppHandler(ctx context.Context, in io.Reader, out io.Writer) { + + fnctx := fdk.Context(ctx) + + var request AppRequest + json.NewDecoder(in).Decode(&request) + + if request.IsDebug { + log.Printf("Received request %v", request) + log.Printf("Received headers %v", fnctx.Header) + log.Printf("Received config %v", fnctx.Config) + } + + // simulate load if requested + if request.SleepTime > 0 { + if request.IsDebug { + log.Printf("Sleeping %d", request.SleepTime) + } + time.Sleep(time.Duration(request.SleepTime) * time.Millisecond) + } + + // simulate crash + if request.IsCrash { + panic("Crash requested") + } + + // custom response code + if request.ResponseCode != 0 { + fdk.WriteStatus(out, request.ResponseCode) + } else { + fdk.WriteStatus(out, 200) + } + + // custom content type + if request.ResponseContentType != "" { + fdk.SetHeader(out, "Content-Type", request.ResponseContentType) + } else { + fdk.SetHeader(out, "Content-Type", "application/json") + } + + resp := AppResponse{ + Request: request, + Headers: fnctx.Header, + Config: fnctx.Config, + } + + json.NewEncoder(out).Encode(&resp) +} + +func main() { + fdk.Handle(fdk.HandlerFunc(AppHandler)) +} diff --git a/images/fn-test-utils/release.sh b/images/fn-test-utils/release.sh new file mode 100755 index 000000000..11e358228 --- /dev/null +++ b/images/fn-test-utils/release.sh @@ -0,0 +1,4 @@ +set -e + +./build.sh +docker push fnproject/fn-test-utils:latest diff --git a/release.sh b/release.sh index 56aa373ab..ca1843f91 100755 --- a/release.sh +++ b/release.sh @@ -45,6 +45,9 @@ docker tag $user/$image:latest $user/$image_deprecated:latest docker push $user/$image_deprecated:$version docker push $user/$image_deprecated:latest +# release test utils docker image +(cd images/fn-test-utils && ./release.sh) + cd fnlb ./release.sh cd .. diff --git a/test.sh b/test.sh index 4711f733f..bb7be564f 100755 --- a/test.sh +++ b/test.sh @@ -29,6 +29,9 @@ docker run --name func-mysql-test -p 3306:3306 -e MYSQL_DATABASE=funcs -e MYSQL_ docker rm -fv func-minio-test || echo No prev minio test container docker run -d -p 9000:9000 --name func-minio-test -e "MINIO_ACCESS_KEY=admin" -e "MINIO_SECRET_KEY=password" minio/minio server /data +# build test image locally first +(cd images/fn-test-utils && ./build.sh) + # pull all images used in tests so that tests don't time out and fail spuriously docker pull fnproject/sleeper docker pull fnproject/error @@ -47,7 +50,7 @@ export POSTGRES_URL="postgres://postgres:root@${POSTGRES_HOST}:${POSTGRES_PORT}/ export MYSQL_URL="mysql://root:root@tcp(${MYSQL_HOST}:${MYSQL_PORT})/funcs" export MINIO_URL="s3://admin:password@${MINIO_HOST}:${MINIO_PORT}/us-east-1/fnlogs" -go test -v $(go list ./... | grep -v vendor | grep -v examples | grep -v test/fn-api-tests) +go test -v $(go list ./... | grep -v vendor | grep -v examples | grep -v test/fn-api-tests | grep -v images/fn-test-utils) go vet $(go list ./... | grep -v vendor) docker rm --force func-postgres-test docker rm --force func-mysql-test