mirror of
https://github.com/openfaas/faas.git
synced 2025-06-08 16:26:47 +00:00
Export new metrics for OpenFaaS Pro scaling
* Add service target metric * Add service min replicas metric * Add scale type metric These combined allow new auto-scaling modes and parameters for OpenFaaS Pro customers. Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
This commit is contained in:
parent
34735d61d0
commit
d85d5e7239
@ -1,6 +1,6 @@
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} teamserverless/license-check:0.3.9 as license-check
|
||||
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.15 as build
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.17 as build
|
||||
|
||||
ENV GO111MODULE=on
|
||||
ENV CGO_ENABLED=0
|
||||
@ -24,7 +24,6 @@ COPY go.sum go.sum
|
||||
COPY handlers handlers
|
||||
COPY metrics metrics
|
||||
COPY requests requests
|
||||
COPY tests tests
|
||||
|
||||
COPY types types
|
||||
COPY plugin plugin
|
||||
|
@ -5,9 +5,7 @@ go 1.16
|
||||
require (
|
||||
github.com/docker/distribution v2.7.1+incompatible
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/nats-io/nats-server/v2 v2.3.2 // indirect
|
||||
github.com/nats-io/nats-streaming-server v0.22.0 // indirect
|
||||
github.com/openfaas/faas-provider v0.18.6
|
||||
github.com/openfaas/faas-provider v0.18.7
|
||||
github.com/openfaas/nats-queue-worker v0.0.0-20210726161954-ada9a31504c9
|
||||
github.com/prometheus/client_golang v1.9.0
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
|
@ -71,7 +71,6 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
@ -93,7 +92,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
@ -105,8 +103,8 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
@ -141,7 +139,6 @@ github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
@ -176,12 +173,9 @@ github.com/klauspost/compress v1.11.12/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdY
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
@ -245,10 +239,9 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/openfaas/faas v0.0.0-20200422113858-a7c6c3920078/go.mod h1:E0m2rLup0Vvxg53BKxGgaYAGcZa3Xl+vvL7vSi5yQ14=
|
||||
github.com/openfaas/faas-provider v0.18.6 h1:wypzvPKZqta8t4rx3W6Dm14ommBCc+rQ4DKDiBdGB7M=
|
||||
github.com/openfaas/faas-provider v0.18.6/go.mod h1:fq1JL0mX4rNvVVvRLaLRJ3H6o667sHuyP5p/7SZEe98=
|
||||
github.com/openfaas/nats-queue-worker v0.0.0-20210726161233-3fa550b705fe/go.mod h1:njdij5dt/fm8EMrpUFiAW5u6tg7higDN+Xi+LXclDas=
|
||||
github.com/openfaas/faas-provider v0.18.7 h1:Oq3N7KrlAkAZ23N5gzZfdA9F62vpUFk3ZkQpQuHeRBU=
|
||||
github.com/openfaas/faas-provider v0.18.7/go.mod h1:S217qfIaMrv+XKJxgbhBzJzCfyFvoIF+BvYdDo6XIDQ=
|
||||
github.com/openfaas/nats-queue-worker v0.0.0-20210726161954-ada9a31504c9 h1:dpG1UcgTesGfLetgT3ns1cAhP8XMDZqxnxTW1MlnwSc=
|
||||
github.com/openfaas/nats-queue-worker v0.0.0-20210726161954-ada9a31504c9/go.mod h1:ajlN2z+D8JPBq3kWNv4WLT6mtKPqlgeE3dYEx39d1tk=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
@ -261,7 +254,6 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||
@ -378,7 +370,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -495,7 +486,6 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
@ -503,7 +493,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
const NameExpression = "-a-zA-Z_0-9."
|
||||
|
||||
func main() {
|
||||
|
||||
if len(version.GitCommitMessage) == 0 {
|
||||
version.GitCommitMessage = "See GitHub for latest changes"
|
||||
}
|
||||
@ -125,7 +126,7 @@ func main() {
|
||||
faasHandlers.DeployFunction = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, nilURLTransformer, serviceAuthInjector)
|
||||
faasHandlers.DeleteFunction = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, nilURLTransformer, serviceAuthInjector)
|
||||
faasHandlers.UpdateFunction = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, nilURLTransformer, serviceAuthInjector)
|
||||
faasHandlers.QueryFunction = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, nilURLTransformer, serviceAuthInjector)
|
||||
faasHandlers.FunctionStatus = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, nilURLTransformer, serviceAuthInjector)
|
||||
|
||||
faasHandlers.InfoHandler = handlers.MakeInfoHandler(handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, nilURLTransformer, serviceAuthInjector))
|
||||
faasHandlers.SecretHandler = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, nilURLTransformer, serviceAuthInjector)
|
||||
@ -198,8 +199,8 @@ func main() {
|
||||
decorateExternalAuth(faasHandlers.ListFunctions, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
faasHandlers.ScaleFunction =
|
||||
decorateExternalAuth(faasHandlers.ScaleFunction, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
faasHandlers.QueryFunction =
|
||||
decorateExternalAuth(faasHandlers.QueryFunction, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
faasHandlers.FunctionStatus =
|
||||
decorateExternalAuth(faasHandlers.FunctionStatus, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
faasHandlers.InfoHandler =
|
||||
decorateExternalAuth(faasHandlers.InfoHandler, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
faasHandlers.AsyncReport =
|
||||
@ -222,7 +223,7 @@ func main() {
|
||||
r.HandleFunc("/system/info", faasHandlers.InfoHandler).Methods(http.MethodGet)
|
||||
r.HandleFunc("/system/alert", faasHandlers.Alert).Methods(http.MethodPost)
|
||||
|
||||
r.HandleFunc("/system/function/{name:["+NameExpression+"]+}", faasHandlers.QueryFunction).Methods(http.MethodGet)
|
||||
r.HandleFunc("/system/function/{name:["+NameExpression+"]+}", faasHandlers.FunctionStatus).Methods(http.MethodGet)
|
||||
r.HandleFunc("/system/functions", faasHandlers.ListFunctions).Methods(http.MethodGet)
|
||||
r.HandleFunc("/system/functions", faasHandlers.DeployFunction).Methods(http.MethodPost)
|
||||
r.HandleFunc("/system/functions", faasHandlers.DeleteFunction).Methods(http.MethodDelete)
|
||||
|
@ -35,9 +35,7 @@ func AddMetricsHandler(handler http.HandlerFunc, prometheusQuery PrometheusQuery
|
||||
recorder.Code,
|
||||
string(upstreamBody))
|
||||
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("List functions responded with code %d", recorder.Code)))
|
||||
http.Error(w, "Unexpected status code retriving functions from backend", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
@ -48,28 +46,33 @@ func AddMetricsHandler(handler http.HandlerFunc, prometheusQuery PrometheusQuery
|
||||
if err != nil {
|
||||
log.Printf("Metrics upstream error: %s", err)
|
||||
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("Error parsing metrics from upstream provider/backend."))
|
||||
http.Error(w, "Error parsing metrics from upstream provider/backend", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
expr := url.QueryEscape(`sum(gateway_function_invocation_total{function_name=~".*", code=~".*"}) by (function_name, code)`)
|
||||
// expr := "sum(gateway_function_invocation_total%7Bfunction_name%3D~%22.*%22%2C+code%3D~%22.*%22%7D)+by+(function_name%2C+code)"
|
||||
results, fetchErr := prometheusQuery.Fetch(expr)
|
||||
if fetchErr != nil {
|
||||
log.Printf("Error querying Prometheus API: %s\n", fetchErr.Error())
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(upstreamBody)
|
||||
return
|
||||
// Ensure values are empty first.
|
||||
for i := range functions {
|
||||
functions[i].InvocationCount = 0
|
||||
}
|
||||
|
||||
mixIn(&functions, results)
|
||||
if len(functions) > 0 {
|
||||
|
||||
bytesOut, marshalErr := json.Marshal(functions)
|
||||
if marshalErr != nil {
|
||||
log.Println(marshalErr)
|
||||
ns := functions[0].Namespace
|
||||
q := fmt.Sprintf(`sum(gateway_function_invocation_total{function_name=~".*.%s"}) by (function_name)`, ns)
|
||||
// Restrict query results to only function names matching namespace suffix.
|
||||
|
||||
results, err := prometheusQuery.Fetch(url.QueryEscape(q))
|
||||
if err != nil {
|
||||
log.Printf("Error querying Prometheus: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
mixIn(&functions, results)
|
||||
}
|
||||
|
||||
bytesOut, err := json.Marshal(functions)
|
||||
if err != nil {
|
||||
log.Printf("Error serializing functions: %s", err)
|
||||
http.Error(w, "error writing response after adding metrics", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
@ -85,25 +88,19 @@ func mixIn(functions *[]types.FunctionStatus, metrics *VectorQueryResponse) {
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure values are empty first.
|
||||
for i := range *functions {
|
||||
(*functions)[i].InvocationCount = 0
|
||||
}
|
||||
|
||||
for i, function := range *functions {
|
||||
for _, v := range metrics.Data.Result {
|
||||
|
||||
if v.Metric.FunctionName == fmt.Sprintf("%s.%s", function.Name, function.Namespace) {
|
||||
metricValue := v.Value[1]
|
||||
switch metricValue.(type) {
|
||||
switch value := metricValue.(type) {
|
||||
case string:
|
||||
f, strconvErr := strconv.ParseFloat(metricValue.(string), 64)
|
||||
if strconvErr != nil {
|
||||
log.Printf("Unable to convert value for metric: %s\n", strconvErr)
|
||||
f, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
log.Printf("add_metrics: unable to convert value %q for metric: %s", value, err)
|
||||
continue
|
||||
}
|
||||
(*functions)[i].InvocationCount += f
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ func Test_FunctionsHandler_ReturnsJSONAndOneFunction(t *testing.T) {
|
||||
func makeFunctionsHandler() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
functions := []types.FunctionStatus{
|
||||
types.FunctionStatus{
|
||||
{
|
||||
Name: "func_echoit",
|
||||
Replicas: 0,
|
||||
Namespace: "openfaas-fn",
|
||||
|
@ -12,12 +12,14 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"log"
|
||||
|
||||
"github.com/openfaas/faas-provider/auth"
|
||||
types "github.com/openfaas/faas-provider/types"
|
||||
"github.com/openfaas/faas/gateway/scaling"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
@ -46,6 +48,7 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
|
||||
e.metricOptions.GatewayFunctionsHistogram.Describe(ch)
|
||||
e.metricOptions.ServiceReplicasGauge.Describe(ch)
|
||||
e.metricOptions.GatewayFunctionInvocationStarted.Describe(ch)
|
||||
e.metricOptions.ServiceTargetLoadGauge.Describe(ch)
|
||||
|
||||
e.metricOptions.ServiceMetrics.Counter.Describe(ch)
|
||||
e.metricOptions.ServiceMetrics.Histogram.Describe(ch)
|
||||
@ -59,6 +62,8 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
||||
e.metricOptions.GatewayFunctionInvocationStarted.Collect(ch)
|
||||
|
||||
e.metricOptions.ServiceReplicasGauge.Reset()
|
||||
e.metricOptions.ServiceTargetLoadGauge.Reset()
|
||||
|
||||
for _, service := range e.services {
|
||||
var serviceName string
|
||||
if len(service.Namespace) > 0 {
|
||||
@ -66,12 +71,54 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
||||
} else {
|
||||
serviceName = service.Name
|
||||
}
|
||||
|
||||
// Set current replica count
|
||||
e.metricOptions.ServiceReplicasGauge.
|
||||
WithLabelValues(serviceName).
|
||||
Set(float64(service.Replicas))
|
||||
|
||||
// Set minimum replicas
|
||||
minReplicas := scaling.DefaultMinReplicas
|
||||
if service.Labels != nil {
|
||||
a := *service.Labels
|
||||
if v, ok := a[scaling.MinScaleLabel]; ok && len(v) > 0 {
|
||||
val, _ := strconv.Atoi(v)
|
||||
minReplicas = val
|
||||
}
|
||||
}
|
||||
|
||||
e.metricOptions.ServiceMinReplicasGauge.
|
||||
WithLabelValues(serviceName).
|
||||
Set(float64(minReplicas))
|
||||
|
||||
// Set scale type
|
||||
scaleType := scaling.DefaultTypeScale
|
||||
if service.Labels != nil {
|
||||
a := *service.Labels
|
||||
if v, ok := a[scaling.ScaleTypeLabel]; ok && len(v) > 0 {
|
||||
scaleType = v
|
||||
}
|
||||
}
|
||||
|
||||
// Set target load
|
||||
targetScale := scaling.DefaultTargetLoad
|
||||
if service.Labels != nil {
|
||||
a := *service.Labels
|
||||
if v, ok := a[scaling.TargetLoadLabel]; ok && len(v) > 0 {
|
||||
val, _ := strconv.Atoi(v)
|
||||
targetScale = val
|
||||
}
|
||||
}
|
||||
|
||||
e.metricOptions.ServiceTargetLoadGauge.
|
||||
WithLabelValues(serviceName, scaleType).
|
||||
Set(float64(targetScale))
|
||||
|
||||
}
|
||||
|
||||
e.metricOptions.ServiceReplicasGauge.Collect(ch)
|
||||
e.metricOptions.ServiceMinReplicasGauge.Collect(ch)
|
||||
e.metricOptions.ServiceTargetLoadGauge.Collect(ch)
|
||||
|
||||
e.metricOptions.ServiceMetrics.Counter.Collect(ch)
|
||||
e.metricOptions.ServiceMetrics.Histogram.Collect(ch)
|
||||
|
@ -44,21 +44,21 @@ func Test_Describe_DescribesThePrometheusMetrics(t *testing.T) {
|
||||
expectedGatewayFunctionInvocationDesc := `Desc{fqName: "gateway_function_invocation_total", help: "Function metrics", constLabels: {}, variableLabels: [function_name code]}`
|
||||
actualGatewayFunctionInvocationDesc := d.String()
|
||||
if expectedGatewayFunctionInvocationDesc != actualGatewayFunctionInvocationDesc {
|
||||
t.Errorf("Want %s, got: %s", expectedGatewayFunctionInvocationDesc, actualGatewayFunctionInvocationDesc)
|
||||
t.Errorf("Want\n%s\ngot\n%s", expectedGatewayFunctionInvocationDesc, actualGatewayFunctionInvocationDesc)
|
||||
}
|
||||
|
||||
d = <-ch
|
||||
expectedGatewayFunctionsHistogramDesc := `Desc{fqName: "gateway_functions_seconds", help: "Function time taken", constLabels: {}, variableLabels: [function_name]}`
|
||||
actualGatewayFunctionsHistogramDesc := d.String()
|
||||
if expectedGatewayFunctionsHistogramDesc != actualGatewayFunctionsHistogramDesc {
|
||||
t.Errorf("Want %s, got: %s", expectedGatewayFunctionsHistogramDesc, actualGatewayFunctionsHistogramDesc)
|
||||
t.Errorf("Want\n%s\ngot\n%s", expectedGatewayFunctionsHistogramDesc, actualGatewayFunctionsHistogramDesc)
|
||||
}
|
||||
|
||||
d = <-ch
|
||||
expectedServiceReplicasGaugeDesc := `Desc{fqName: "gateway_service_count", help: "Service replicas", constLabels: {}, variableLabels: [function_name]}`
|
||||
expectedServiceReplicasGaugeDesc := `Desc{fqName: "gateway_service_count", help: "Current count of replicas for function", constLabels: {}, variableLabels: [function_name]}`
|
||||
actualServiceReplicasGaugeDesc := d.String()
|
||||
if expectedServiceReplicasGaugeDesc != actualServiceReplicasGaugeDesc {
|
||||
t.Errorf("Want %s, got: %s", expectedServiceReplicasGaugeDesc, actualServiceReplicasGaugeDesc)
|
||||
t.Errorf("Want\n%s\ngot\n%s", expectedServiceReplicasGaugeDesc, actualServiceReplicasGaugeDesc)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,8 +16,12 @@ type MetricOptions struct {
|
||||
GatewayFunctionInvocation *prometheus.CounterVec
|
||||
GatewayFunctionsHistogram *prometheus.HistogramVec
|
||||
GatewayFunctionInvocationStarted *prometheus.CounterVec
|
||||
ServiceReplicasGauge *prometheus.GaugeVec
|
||||
ServiceMetrics *ServiceMetricOptions
|
||||
|
||||
ServiceReplicasGauge *prometheus.GaugeVec
|
||||
ServiceMinReplicasGauge *prometheus.GaugeVec
|
||||
ServiceTargetLoadGauge *prometheus.GaugeVec
|
||||
|
||||
ServiceMetrics *ServiceMetricOptions
|
||||
}
|
||||
|
||||
// ServiceMetricOptions provides RED metrics
|
||||
@ -62,11 +66,29 @@ func BuildMetricsOptions() MetricOptions {
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "gateway",
|
||||
Name: "service_count",
|
||||
Help: "Service replicas",
|
||||
Help: "Current count of replicas for function",
|
||||
},
|
||||
[]string{"function_name"},
|
||||
)
|
||||
|
||||
serviceMinReplicas := prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "gateway",
|
||||
Name: "service_min",
|
||||
Help: "Minium replicas for function",
|
||||
},
|
||||
[]string{"function_name"},
|
||||
)
|
||||
|
||||
serviceTargetLoad := prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "gateway",
|
||||
Name: "service_target_load",
|
||||
Help: "Target load for function",
|
||||
},
|
||||
[]string{"function_name", "scaling_type"},
|
||||
)
|
||||
|
||||
// For automatic monitoring and alerting (RED method)
|
||||
histogram := prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Subsystem: "http",
|
||||
@ -104,6 +126,8 @@ func BuildMetricsOptions() MetricOptions {
|
||||
GatewayFunctionsHistogram: gatewayFunctionsHistogram,
|
||||
GatewayFunctionInvocation: gatewayFunctionInvocation,
|
||||
ServiceReplicasGauge: serviceReplicas,
|
||||
ServiceMinReplicasGauge: serviceMinReplicas,
|
||||
ServiceTargetLoadGauge: serviceTargetLoad,
|
||||
ServiceMetrics: serviceMetricOptions,
|
||||
GatewayFunctionInvocationStarted: gatewayFunctionInvocationStarted,
|
||||
}
|
||||
|
@ -100,12 +100,15 @@ func (s ExternalServiceQuery) GetReplicas(serviceName, serviceNamespace string)
|
||||
scalingFactor := uint64(scaling.DefaultScalingFactor)
|
||||
availableReplicas := function.AvailableReplicas
|
||||
|
||||
targetLoad := uint64(scaling.DefaultTargetLoad)
|
||||
|
||||
if function.Labels != nil {
|
||||
labels := *function.Labels
|
||||
|
||||
minReplicas = extractLabelValue(labels[scaling.MinScaleLabel], minReplicas)
|
||||
maxReplicas = extractLabelValue(labels[scaling.MaxScaleLabel], maxReplicas)
|
||||
extractedScalingFactor := extractLabelValue(labels[scaling.ScalingFactorLabel], scalingFactor)
|
||||
targetLoad = extractLabelValue(labels[scaling.TargetLoadLabel], targetLoad)
|
||||
|
||||
if extractedScalingFactor >= 0 && extractedScalingFactor <= 100 {
|
||||
scalingFactor = extractedScalingFactor
|
||||
@ -113,6 +116,7 @@ func (s ExternalServiceQuery) GetReplicas(serviceName, serviceNamespace string)
|
||||
log.Printf("Bad Scaling Factor: %d, is not in range of [0 - 100]. Will fallback to %d", extractedScalingFactor, scalingFactor)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("GetReplicas [%s.%s] took: %fs", serviceName, serviceNamespace, time.Since(start).Seconds())
|
||||
|
||||
return scaling.ServiceQueryResponse{
|
||||
@ -122,6 +126,7 @@ func (s ExternalServiceQuery) GetReplicas(serviceName, serviceNamespace string)
|
||||
ScalingFactor: scalingFactor,
|
||||
AvailableReplicas: availableReplicas,
|
||||
Annotations: function.Annotations,
|
||||
TargetLoad: targetLoad,
|
||||
}, err
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,7 @@ func TestGetReplicasExistentFn(t *testing.T) {
|
||||
MinReplicas: uint64(scaling.DefaultMinReplicas),
|
||||
ScalingFactor: uint64(scaling.DefaultScalingFactor),
|
||||
AvailableReplicas: 0,
|
||||
TargetLoad: 10,
|
||||
}
|
||||
|
||||
var injector middleware.AuthInjector
|
||||
@ -89,7 +90,7 @@ func TestGetReplicasExistentFn(t *testing.T) {
|
||||
t.Fail()
|
||||
}
|
||||
if svcQryResp != expectedSvcQryResp {
|
||||
t.Logf("Unexpected return values - wanted %+v, got: %+v ", expectedSvcQryResp, svcQryResp)
|
||||
t.Logf("Unexpected return values - wanted\n%+v\ngot\n%+v ", expectedSvcQryResp, svcQryResp)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,11 @@ const (
|
||||
// DefaultScalingFactor is the defining proportion for the scaling increments.
|
||||
DefaultScalingFactor = 20
|
||||
|
||||
// DefaultTargetLoad
|
||||
DefaultTargetLoad = 10
|
||||
|
||||
DefaultTypeScale = "rps"
|
||||
|
||||
// MinScaleLabel label indicating min scale for a function
|
||||
MinScaleLabel = "com.openfaas.scale.min"
|
||||
|
||||
@ -18,4 +23,10 @@ const (
|
||||
|
||||
// ScalingFactorLabel label indicates the scaling factor for a function
|
||||
ScalingFactorLabel = "com.openfaas.scale.factor"
|
||||
|
||||
// TargetLoadLabel see also DefaultTargetScale
|
||||
TargetLoadLabel = "com.openfaas.scale.target"
|
||||
|
||||
// ScaleTypeLabel see also DefaultScaleType
|
||||
ScaleTypeLabel = "com.openfaas.scale.type"
|
||||
)
|
||||
|
@ -17,4 +17,5 @@ type ServiceQueryResponse struct {
|
||||
ScalingFactor uint64
|
||||
AvailableReplicas uint64
|
||||
Annotations *map[string]string
|
||||
TargetLoad uint64
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
# Integration testing
|
||||
|
||||
These tests should be run against the sample stack included in the repository root.
|
||||
|
||||
## Deploy the stack
|
||||
```
|
||||
./deploy_stack.sh
|
||||
faas-cli deploy -f ./stack.yml
|
||||
```
|
||||
|
||||
## Remove the stack
|
||||
1. Delete all OpenFaaS deployed functions
|
||||
```
|
||||
faas-cli remove
|
||||
docker stack rm func
|
||||
```
|
@ -1,88 +0,0 @@
|
||||
// Copyright (c) Alex Ellis 2017. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
package inttests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
types "github.com/openfaas/faas-provider/types"
|
||||
requests "github.com/openfaas/faas/gateway/requests"
|
||||
)
|
||||
|
||||
func createFunction(request types.FunctionDeployment) (string, int, error) {
|
||||
marshalled, _ := json.Marshal(request)
|
||||
return fireRequest("http://localhost:8080/system/functions", http.MethodPost, string(marshalled))
|
||||
}
|
||||
|
||||
func deleteFunction(name string) (string, int, error) {
|
||||
marshalled, _ := json.Marshal(requests.DeleteFunctionRequest{FunctionName: name})
|
||||
return fireRequest("http://localhost:8080/system/functions", http.MethodDelete, string(marshalled))
|
||||
}
|
||||
|
||||
func TestCreate_ValidRequest(t *testing.T) {
|
||||
request := types.FunctionDeployment{
|
||||
Service: "test_resizer",
|
||||
Image: "functions/resizer",
|
||||
EnvProcess: "",
|
||||
}
|
||||
|
||||
_, code, err := createFunction(request)
|
||||
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
expectedErrorCode := http.StatusAccepted
|
||||
if code != expectedErrorCode {
|
||||
t.Errorf("Got HTTP code: %d, want %d\n", code, expectedErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
deleteFunction("test_resizer")
|
||||
}
|
||||
|
||||
func TestCreate_InvalidImage(t *testing.T) {
|
||||
request := types.FunctionDeployment{
|
||||
Service: "test_resizer",
|
||||
Image: "a b c",
|
||||
EnvProcess: "",
|
||||
}
|
||||
|
||||
body, code, err := createFunction(request)
|
||||
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
expectedErrorCode := http.StatusBadRequest
|
||||
if code != expectedErrorCode {
|
||||
t.Errorf("Got HTTP code: %d, want %d\n", code, expectedErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
expectedErrorSlice := "is not a valid repository/tag"
|
||||
if !strings.Contains(body, expectedErrorSlice) {
|
||||
t.Errorf("Error message %s does not contain: %s\n", body, expectedErrorSlice)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate_InvalidJson(t *testing.T) {
|
||||
reqBody := `not json`
|
||||
_, code, err := fireRequest("http://localhost:8080/system/functions", http.MethodPost, reqBody)
|
||||
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if code != http.StatusBadRequest {
|
||||
t.Errorf("Got HTTP code: %d, want %d\n", code, http.StatusBadRequest)
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
// Copyright (c) Alex Ellis 2017. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
package inttests
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDelete_EmptyFunctionGivenFails(t *testing.T) {
|
||||
reqBody := `{"functionName":""}`
|
||||
_, code, err := fireRequest("http://localhost:8080/system/functions", http.MethodDelete, reqBody)
|
||||
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if code != http.StatusBadRequest {
|
||||
t.Errorf("Got HTTP code: %d, want %d\n", code, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelete_NonExistingFunctionGives404(t *testing.T) {
|
||||
reqBody := `{"functionName":"does_not_exist"}`
|
||||
_, code, err := fireRequest("http://localhost:8080/system/functions", http.MethodDelete, reqBody)
|
||||
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if code != http.StatusNotFound {
|
||||
t.Errorf("Got HTTP code: %d, want %d\n", code, http.StatusNotFound)
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
package inttests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/openfaas/faas/gateway/types"
|
||||
)
|
||||
|
||||
func Test_InfoEndpoint_Returns_200(t *testing.T) {
|
||||
_, code, err := fireRequest("http://localhost:8080/system/info", http.MethodGet, "")
|
||||
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
wantCode := http.StatusOK
|
||||
if code != wantCode {
|
||||
t.Errorf("status code, want: %d, got: %d", wantCode, code)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_InfoEndpoint_Returns_Gateway_Version_SHA_And_Message(t *testing.T) {
|
||||
body, _, err := fireRequest("http://localhost:8080/system/info", http.MethodGet, "")
|
||||
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
gatewayInfo := &types.GatewayInfo{}
|
||||
err = json.Unmarshal([]byte(body), gatewayInfo)
|
||||
if err != nil {
|
||||
t.Errorf("Could not unmarshal gateway info, response body:%s, error:%s", body, err.Error())
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if len(gatewayInfo.Version.SHA) != 40 {
|
||||
t.Errorf("length of SHA incorrect, want: %d, got: %d. Json body was %s", 40, len(gatewayInfo.Version.SHA), body)
|
||||
}
|
||||
|
||||
if len(gatewayInfo.Version.CommitMessage) == 0 {
|
||||
t.Errorf("length of commit message should be greater than 0. Json body was %s", body)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_InfoEndpoint_Returns_Arch(t *testing.T) {
|
||||
body, _, err := fireRequest("http://localhost:8080/system/info", http.MethodGet, "")
|
||||
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
gatewayInfo := &types.GatewayInfo{}
|
||||
err = json.Unmarshal([]byte(body), gatewayInfo)
|
||||
if err != nil {
|
||||
t.Errorf("Could not unmarshal gateway info, response body:%s, error:%s", body, err.Error())
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if len(gatewayInfo.Arch) == 0 {
|
||||
t.Errorf("value of arch should be non-empty")
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
// Copyright (c) Alex Ellis 2017. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
package inttests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Before running these tests do a Docker stack deploy.
|
||||
|
||||
func fireRequest(url string, method string, reqBody string) (string, int, error) {
|
||||
headers := make(map[string]string)
|
||||
return fireRequestWithHeaders(url, method, reqBody, headers)
|
||||
}
|
||||
|
||||
func fireRequestWithHeaders(url string, method string, reqBody string, headers map[string]string) (string, int, error) {
|
||||
httpClient := http.Client{
|
||||
Timeout: time.Second * 2, // Maximum of 2 secs
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url, bytes.NewBufferString(reqBody))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", "go-integration")
|
||||
for kk, vv := range headers {
|
||||
req.Header.Set(kk, vv)
|
||||
}
|
||||
|
||||
res, getErr := httpClient.Do(req)
|
||||
if getErr != nil {
|
||||
log.Fatal(getErr)
|
||||
}
|
||||
|
||||
body, readErr := ioutil.ReadAll(res.Body)
|
||||
defer req.Body.Close()
|
||||
if readErr != nil {
|
||||
log.Fatal(readErr)
|
||||
}
|
||||
return string(body), res.StatusCode, readErr
|
||||
}
|
||||
|
||||
func TestGet_Rejected(t *testing.T) {
|
||||
var reqBody string
|
||||
unsupportedMethod := http.MethodHead
|
||||
_, code, err := fireRequest("http://localhost:8080/function/echoit", unsupportedMethod, reqBody)
|
||||
want := http.StatusMethodNotAllowed
|
||||
if code != want {
|
||||
t.Logf("Failed got: %d, wanted: %d", code, want)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestEchoIt_Post_Route_Handler_ForwardsClientHeaders(t *testing.T) {
|
||||
reqBody := "test message"
|
||||
headers := make(map[string]string, 0)
|
||||
headers["X-Api-Key"] = "123"
|
||||
|
||||
body, code, err := fireRequestWithHeaders("http://localhost:8080/function/echoit", http.MethodPost, reqBody, headers)
|
||||
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if code != http.StatusOK {
|
||||
t.Logf("Failed, code: %d, body:%s", code, body)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if body != reqBody {
|
||||
t.Log("Expected body returned")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestEchoIt_Post_Route_Handler(t *testing.T) {
|
||||
reqBody := "test message"
|
||||
body, code, err := fireRequest("http://localhost:8080/function/echoit", http.MethodPost, reqBody)
|
||||
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
if code != http.StatusOK {
|
||||
t.Log("Failed")
|
||||
}
|
||||
if body != reqBody {
|
||||
t.Log("Expected body returned")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
// Test suppressed due to X-Header deprecation.
|
||||
// func TestEchoIt_Post_X_Header_Routing_Handler(t *testing.T) {
|
||||
// reqBody := "test message"
|
||||
// headers := make(map[string]string, 0)
|
||||
// headers["X-Function"] = "func_echoit"
|
||||
|
||||
// body, code, err := fireRequestWithHeaders("http://localhost:8080/", http.MethodPost, reqBody, headers)
|
||||
|
||||
// if err != nil {
|
||||
// t.Log(err)
|
||||
// t.Fail()
|
||||
// }
|
||||
// if code != http.StatusOK {
|
||||
// t.Logf("statusCode - want: %d, got: %d", http.StatusOK, code)
|
||||
// }
|
||||
// if body != reqBody {
|
||||
// t.Logf("Expected body from echo function to be equal to input, but was: %s", body)
|
||||
// t.Fail()
|
||||
// }
|
||||
// }
|
@ -4,18 +4,24 @@ import "net/http"
|
||||
|
||||
// HandlerSet can be initialized with handlers for binding to mux
|
||||
type HandlerSet struct {
|
||||
// Proxy invokes functions upstream
|
||||
// Proxy invokes a function
|
||||
Proxy http.HandlerFunc
|
||||
|
||||
// DeployFunction deploys a new function that isn't already deployed
|
||||
DeployFunction http.HandlerFunc
|
||||
DeleteFunction http.HandlerFunc
|
||||
ListFunctions http.HandlerFunc
|
||||
Alert http.HandlerFunc
|
||||
|
||||
// DeleteFunction deletes a function that is already deployed
|
||||
DeleteFunction http.HandlerFunc
|
||||
|
||||
// ListFunctions lists all deployed functions in a namespace
|
||||
ListFunctions http.HandlerFunc
|
||||
Alert http.HandlerFunc
|
||||
|
||||
// UpdateFunction updates an existing function
|
||||
UpdateFunction http.HandlerFunc
|
||||
|
||||
// QueryFunction queries the metdata for a function
|
||||
QueryFunction http.HandlerFunc
|
||||
// FunctionStatus returns the status of an already deployed function
|
||||
FunctionStatus http.HandlerFunc
|
||||
|
||||
// QueuedProxy queue work and return synchronous response
|
||||
QueuedProxy http.HandlerFunc
|
||||
|
51
gateway/vendor/github.com/openfaas/faas-provider/types/function_deployment.go
generated
vendored
Normal file
51
gateway/vendor/github.com/openfaas/faas-provider/types/function_deployment.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
package types
|
||||
|
||||
// FunctionDeployment represents a request to create or update a Function.
|
||||
type FunctionDeployment struct {
|
||||
|
||||
// Service is the name of the function deployment
|
||||
Service string `json:"service"`
|
||||
|
||||
// Image is a fully-qualified container image
|
||||
Image string `json:"image"`
|
||||
|
||||
// Namespace for the function, if supported by the faas-provider
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
|
||||
// EnvProcess overrides the fprocess environment variable and can be used
|
||||
// with the watchdog
|
||||
EnvProcess string `json:"envProcess,omitempty"`
|
||||
|
||||
// EnvVars can be provided to set environment variables for the function runtime.
|
||||
EnvVars map[string]string `json:"envVars,omitempty"`
|
||||
|
||||
// Constraints are specific to the faas-provider.
|
||||
Constraints []string `json:"constraints,omitempty"`
|
||||
|
||||
// Secrets list of secrets to be made available to function
|
||||
Secrets []string `json:"secrets,omitempty"`
|
||||
|
||||
// Labels are metadata for functions which may be used by the
|
||||
// faas-provider or the gateway
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
|
||||
// Annotations are metadata for functions which may be used by the
|
||||
// faas-provider or the gateway
|
||||
Annotations *map[string]string `json:"annotations,omitempty"`
|
||||
|
||||
// Limits for function
|
||||
Limits *FunctionResources `json:"limits,omitempty"`
|
||||
|
||||
// Requests of resources requested by function
|
||||
Requests *FunctionResources `json:"requests,omitempty"`
|
||||
|
||||
// ReadOnlyRootFilesystem removes write-access from the root filesystem
|
||||
// mount-point.
|
||||
ReadOnlyRootFilesystem bool `json:"readOnlyRootFilesystem,omitempty"`
|
||||
}
|
||||
|
||||
// FunctionResources Memory and CPU
|
||||
type FunctionResources struct {
|
||||
Memory string `json:"memory,omitempty"`
|
||||
CPU string `json:"cpu,omitempty"`
|
||||
}
|
@ -2,72 +2,6 @@ package types
|
||||
|
||||
import "time"
|
||||
|
||||
// Secret for underlying orchestrator
|
||||
type Secret struct {
|
||||
// Name of the secret
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace if applicable for the secret
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
|
||||
// Value is a string representing the string's value
|
||||
Value string `json:"value,omitempty"`
|
||||
|
||||
// RawValue can be used to provide binary data when
|
||||
// Value is not set
|
||||
RawValue []byte `json:"rawValue,omitempty"`
|
||||
}
|
||||
|
||||
// FunctionDeployment represents a request to create or update a Function.
|
||||
type FunctionDeployment struct {
|
||||
|
||||
// Service is the name of the function deployment
|
||||
Service string `json:"service"`
|
||||
|
||||
// Image is a fully-qualified container image
|
||||
Image string `json:"image"`
|
||||
|
||||
// Namespace for the function, if supported by the faas-provider
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
|
||||
// EnvProcess overrides the fprocess environment variable and can be used
|
||||
// with the watchdog
|
||||
EnvProcess string `json:"envProcess,omitempty"`
|
||||
|
||||
// EnvVars can be provided to set environment variables for the function runtime.
|
||||
EnvVars map[string]string `json:"envVars,omitempty"`
|
||||
|
||||
// Constraints are specific to the faas-provider.
|
||||
Constraints []string `json:"constraints,omitempty"`
|
||||
|
||||
// Secrets list of secrets to be made available to function
|
||||
Secrets []string `json:"secrets,omitempty"`
|
||||
|
||||
// Labels are metadata for functions which may be used by the
|
||||
// faas-provider or the gateway
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
|
||||
// Annotations are metadata for functions which may be used by the
|
||||
// faas-provider or the gateway
|
||||
Annotations *map[string]string `json:"annotations,omitempty"`
|
||||
|
||||
// Limits for function
|
||||
Limits *FunctionResources `json:"limits,omitempty"`
|
||||
|
||||
// Requests of resources requested by function
|
||||
Requests *FunctionResources `json:"requests,omitempty"`
|
||||
|
||||
// ReadOnlyRootFilesystem removes write-access from the root filesystem
|
||||
// mount-point.
|
||||
ReadOnlyRootFilesystem bool `json:"readOnlyRootFilesystem,omitempty"`
|
||||
}
|
||||
|
||||
// FunctionResources Memory and CPU
|
||||
type FunctionResources struct {
|
||||
Memory string `json:"memory,omitempty"`
|
||||
CPU string `json:"cpu,omitempty"`
|
||||
}
|
||||
|
||||
// FunctionStatus exported for system/functions endpoint
|
||||
type FunctionStatus struct {
|
||||
|
||||
@ -128,4 +62,24 @@ type FunctionStatus struct {
|
||||
// CreatedAt is the time read back from the faas backend's
|
||||
// data store for when the function or its container was created.
|
||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||
|
||||
// Utilisation represents CPU and RAM used by all of the
|
||||
// functions' replicas. Divide by AvailableReplicas for an
|
||||
// average value per replica.
|
||||
Utilisation FunctionUtilisation `json:"usage,omitempty"`
|
||||
}
|
||||
|
||||
// FunctionUtilisation represents CPU and RAM used by all of the
|
||||
// functions' replicas.
|
||||
//
|
||||
// CPU is measured in seconds consumed since the last measurement
|
||||
// RAM is measured in total bytes consumed
|
||||
//
|
||||
type FunctionUtilisation struct {
|
||||
// CPU is the increase in CPU usage since the last measurement
|
||||
// equivalent to Kubernetes' concept of millicores.
|
||||
CPU float64 `json:"cpu,omitempty"`
|
||||
|
||||
//TotalMemoryBytes is the total memory usage in bytes.
|
||||
TotalMemoryBytes float64 `json:"totalMemoryBytes,omitempty"`
|
||||
}
|
17
gateway/vendor/github.com/openfaas/faas-provider/types/secret.go
generated
vendored
Normal file
17
gateway/vendor/github.com/openfaas/faas-provider/types/secret.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
package types
|
||||
|
||||
// Secret for underlying orchestrator
|
||||
type Secret struct {
|
||||
// Name of the secret
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace if applicable for the secret
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
|
||||
// Value is a string representing the string's value
|
||||
Value string `json:"value,omitempty"`
|
||||
|
||||
// RawValue can be used to provide binary data when
|
||||
// Value is not set
|
||||
RawValue []byte `json:"rawValue,omitempty"`
|
||||
}
|
6
gateway/vendor/modules.txt
vendored
6
gateway/vendor/modules.txt
vendored
@ -20,10 +20,6 @@ github.com/golang/protobuf/ptypes/timestamp
|
||||
github.com/gorilla/mux
|
||||
# github.com/matttproud/golang_protobuf_extensions v1.0.1
|
||||
github.com/matttproud/golang_protobuf_extensions/pbutil
|
||||
# github.com/nats-io/nats-server/v2 v2.3.2
|
||||
## explicit
|
||||
# github.com/nats-io/nats-streaming-server v0.22.0
|
||||
## explicit
|
||||
# github.com/nats-io/nats.go v1.11.1-0.20210623165838-4b75fc59ae30
|
||||
github.com/nats-io/nats.go
|
||||
github.com/nats-io/nats.go/encoders/builtin
|
||||
@ -35,7 +31,7 @@ github.com/nats-io/nuid
|
||||
# github.com/nats-io/stan.go v0.9.0
|
||||
github.com/nats-io/stan.go
|
||||
github.com/nats-io/stan.go/pb
|
||||
# github.com/openfaas/faas-provider v0.18.6
|
||||
# github.com/openfaas/faas-provider v0.18.7
|
||||
## explicit
|
||||
github.com/openfaas/faas-provider/auth
|
||||
github.com/openfaas/faas-provider/types
|
||||
|
Loading…
x
Reference in New Issue
Block a user