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:
Alex Ellis (OpenFaaS Ltd) 2022-01-24 12:11:54 +00:00 committed by Alex Ellis
parent 34735d61d0
commit d85d5e7239
23 changed files with 235 additions and 472 deletions

View File

@ -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

View File

@ -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

View File

@ -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=

View File

@ -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)

View File

@ -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
}
}
}

View File

@ -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",

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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,
}

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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"
)

View File

@ -17,4 +17,5 @@ type ServiceQueryResponse struct {
ScalingFactor uint64
AvailableReplicas uint64
Annotations *map[string]string
TargetLoad uint64
}

View File

@ -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
```

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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")
}
}

View File

@ -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()
// }
// }

View File

@ -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

View 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"`
}

View File

@ -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"`
}

View 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"`
}

View File

@ -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