diff --git a/gateway/Dockerfile b/gateway/Dockerfile index 4ba2034d..c5a2ce44 100644 --- a/gateway/Dockerfile +++ b/gateway/Dockerfile @@ -29,6 +29,7 @@ COPY types types COPY plugin plugin COPY version version COPY scaling scaling +COPY probing probing COPY pkg pkg COPY main.go . diff --git a/gateway/go.mod b/gateway/go.mod index 7ba281b8..a85850be 100644 --- a/gateway/go.mod +++ b/gateway/go.mod @@ -1,15 +1,34 @@ module github.com/openfaas/faas/gateway -go 1.16 +go 1.17 require ( github.com/docker/distribution v2.8.1+incompatible github.com/gorilla/mux v1.8.0 - github.com/hashicorp/golang-lru v0.5.1 // indirect 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.11.1 github.com/prometheus/client_model v0.2.0 go.uber.org/goleak v1.1.10 - golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect + golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/hashicorp/golang-lru v0.5.1 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/nats-io/nats.go v1.11.1-0.20210623165838-4b75fc59ae30 // indirect + github.com/nats-io/nkeys v0.3.0 // indirect + github.com/nats-io/nuid v1.0.1 // indirect + github.com/nats-io/stan.go v0.9.0 // indirect + github.com/prometheus/common v0.26.0 // indirect + github.com/prometheus/procfs v0.6.0 // indirect + golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect + golang.org/x/lint v0.0.0-20190930215403-16217165b5de // indirect + golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect + golang.org/x/tools v0.0.0-20210106214847-113979e3529a // indirect + google.golang.org/protobuf v1.26.0 // indirect ) diff --git a/gateway/handlers/alerthandler.go b/gateway/handlers/alerthandler.go index 45360f95..0cabdec5 100644 --- a/gateway/handlers/alerthandler.go +++ b/gateway/handlers/alerthandler.go @@ -11,6 +11,7 @@ import ( "math" "net/http" + "github.com/openfaas/faas/gateway/pkg/middleware" "github.com/openfaas/faas/gateway/requests" "github.com/openfaas/faas/gateway/scaling" ) @@ -19,23 +20,24 @@ import ( func MakeAlertHandler(service scaling.ServiceQuery, defaultNamespace string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - log.Println("Alert received.") + if r.Body == nil { + http.Error(w, "A body is required for this endpoint", http.StatusBadRequest) + return + } - body, readErr := ioutil.ReadAll(r.Body) + defer r.Body.Close() - log.Println(string(body)) - - if readErr != nil { + body, err := ioutil.ReadAll(r.Body) + if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Unable to read alert.")) - log.Println(readErr) + log.Println(err) return } var req requests.PrometheusAlert - err := json.Unmarshal(body, &req) - if err != nil { + if err := json.Unmarshal(body, &req); err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Unable to parse alert, bad format.")) log.Println(err) @@ -73,7 +75,7 @@ func handleAlerts(req *requests.PrometheusAlert, service scaling.ServiceQuery, d func scaleService(alert requests.PrometheusInnerAlert, service scaling.ServiceQuery, defaultNamespace string) error { var err error - serviceName, namespace := getNamespace(defaultNamespace, alert.Labels.FunctionName) + serviceName, namespace := middleware.GetNamespace(defaultNamespace, alert.Labels.FunctionName) if len(serviceName) > 0 { queryResponse, getErr := service.GetReplicas(serviceName, namespace) diff --git a/gateway/handlers/baseurlresolver_test.go b/gateway/handlers/baseurlresolver_test.go deleted file mode 100644 index 318cba37..00000000 --- a/gateway/handlers/baseurlresolver_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) OpenFaaS Author(s). All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -package handlers - -import ( - "fmt" - "log" - "net/http" - "net/url" - "strings" - "testing" -) - -func TestSingleHostBaseURLResolver(t *testing.T) { - - urlVal, _ := url.Parse("http://upstream:8080/") - r := SingleHostBaseURLResolver{BaseURL: urlVal.String()} - - req, _ := http.NewRequest(http.MethodGet, "http://localhost/function/hello", nil) - - resolved := r.Resolve(req) - want := "http://upstream:8080" - if resolved != want { - t.Logf("r.Resolve failed, want: %s got: %s", want, resolved) - t.Fail() - } -} - -const watchdogPort = 8080 - -func TestFunctionAsHostBaseURLResolver_WithNamespaceOverride(t *testing.T) { - - suffix := "openfaas-fn.local.cluster.svc." - namespace := "openfaas-fn" - newNS := "production-fn" - - r := FunctionAsHostBaseURLResolver{FunctionSuffix: suffix, FunctionNamespace: namespace} - - req, _ := http.NewRequest(http.MethodGet, "http://localhost/function/hello."+newNS, nil) - - resolved := r.Resolve(req) - - newSuffix := strings.Replace(suffix, namespace, newNS, -1) - - want := fmt.Sprintf("http://hello.%s:%d", newSuffix, watchdogPort) - log.Println(want) - if resolved != want { - t.Logf("r.Resolve failed, want: %s got: %s", want, resolved) - t.Fail() - } -} - -func TestFunctionAsHostBaseURLResolver_WithSuffix(t *testing.T) { - suffix := "openfaas-fn.local.cluster.svc." - r := FunctionAsHostBaseURLResolver{FunctionSuffix: suffix} - - req, _ := http.NewRequest(http.MethodGet, "http://localhost/function/hello", nil) - - resolved := r.Resolve(req) - want := fmt.Sprintf("http://hello.%s:%d", suffix, watchdogPort) - log.Println(want) - if resolved != want { - t.Logf("r.Resolve failed, want: %s got: %s", want, resolved) - t.Fail() - } -} - -func TestFunctionAsHostBaseURLResolver_WithoutSuffix(t *testing.T) { - suffix := "" - r := FunctionAsHostBaseURLResolver{FunctionSuffix: suffix} - - req, _ := http.NewRequest(http.MethodGet, "http://localhost/function/hello", nil) - - resolved := r.Resolve(req) - want := fmt.Sprintf("http://hello%s:%d", suffix, watchdogPort) - - if resolved != want { - t.Logf("r.Resolve failed, want: %s got: %s", want, resolved) - t.Fail() - } -} diff --git a/gateway/handlers/forwarding_proxy.go b/gateway/handlers/forwarding_proxy.go index b1dabdc9..3aabd74a 100644 --- a/gateway/handlers/forwarding_proxy.go +++ b/gateway/handlers/forwarding_proxy.go @@ -10,40 +10,17 @@ import ( "log" "net/http" "os" - "regexp" - "strings" "time" "github.com/openfaas/faas/gateway/pkg/middleware" "github.com/openfaas/faas/gateway/types" ) -// functionMatcher parses out the service name (group 1) and rest of path (group 2). -var functionMatcher = regexp.MustCompile("^/?(?:async-)?function/([^/?]+)([^?]*)") - -// Indices and meta-data for functionMatcher regex parts -const ( - hasPathCount = 3 - routeIndex = 0 // routeIndex corresponds to /function/ or /async-function/ - nameIndex = 1 // nameIndex is the function name - pathIndex = 2 // pathIndex is the path i.e. /employee/:id/ -) - -// BaseURLResolver URL resolver for upstream requests -type BaseURLResolver interface { - Resolve(r *http.Request) string -} - -// URLPathTransformer Transform the incoming URL path for upstream requests -type URLPathTransformer interface { - Transform(r *http.Request) string -} - // MakeForwardingProxyHandler create a handler which forwards HTTP requests func MakeForwardingProxyHandler(proxy *types.HTTPClientReverseProxy, notifiers []HTTPNotifier, - baseURLResolver BaseURLResolver, - urlPathTransformer URLPathTransformer, + baseURLResolver middleware.BaseURLResolver, + urlPathTransformer middleware.URLPathTransformer, serviceAuthInjector middleware.AuthInjector) http.HandlerFunc { writeRequestURI := false @@ -165,80 +142,6 @@ func deleteHeaders(target *http.Header, exclude *[]string) { } } -// SingleHostBaseURLResolver resolves URLs against a single BaseURL -type SingleHostBaseURLResolver struct { - BaseURL string -} - -// Resolve the base URL for a request -func (s SingleHostBaseURLResolver) Resolve(r *http.Request) string { - - baseURL := s.BaseURL - - if strings.HasSuffix(baseURL, "/") { - baseURL = baseURL[0 : len(baseURL)-1] - } - return baseURL -} - -// FunctionAsHostBaseURLResolver resolves URLs using a function from the URL as a host -type FunctionAsHostBaseURLResolver struct { - FunctionSuffix string - FunctionNamespace string -} - -// Resolve the base URL for a request -func (f FunctionAsHostBaseURLResolver) Resolve(r *http.Request) string { - svcName := getServiceName(r.URL.Path) - - const watchdogPort = 8080 - var suffix string - - if len(f.FunctionSuffix) > 0 { - if index := strings.LastIndex(svcName, "."); index > -1 && len(svcName) > index+1 { - suffix = strings.Replace(f.FunctionSuffix, f.FunctionNamespace, "", -1) - } else { - suffix = "." + f.FunctionSuffix - } - } - - return fmt.Sprintf("http://%s%s:%d", svcName, suffix, watchdogPort) -} - -// TransparentURLPathTransformer passes the requested URL path through untouched. -type TransparentURLPathTransformer struct { -} - -// Transform returns the URL path unchanged. -func (f TransparentURLPathTransformer) Transform(r *http.Request) string { - return r.URL.Path -} - -// FunctionPrefixTrimmingURLPathTransformer removes the "/function/servicename/" prefix from the URL path. -type FunctionPrefixTrimmingURLPathTransformer struct { -} - -// Transform removes the "/function/servicename/" prefix from the URL path. -func (f FunctionPrefixTrimmingURLPathTransformer) Transform(r *http.Request) string { - ret := r.URL.Path - - if ret != "" { - // When forwarding to a function, since the `/function/xyz` portion - // of a path like `/function/xyz/rest/of/path` is only used or needed - // by the Gateway, we want to trim it down to `/rest/of/path` for the - // upstream request. In the following regex, in the case of a match - // the r.URL.Path will be at `0`, the function name at `1` and the - // rest of the path (the part we are interested in) at `2`. - matcher := functionMatcher.Copy() - parts := matcher.FindStringSubmatch(ret) - if len(parts) == hasPathCount { - ret = parts[pathIndex] - } - } - - return ret -} - // Hop-by-hop headers. These are removed when sent to the backend. // As of RFC 7230, hop-by-hop headers are required to appear in the // Connection header field. These are the headers defined by the diff --git a/gateway/handlers/forwarding_proxy_test.go b/gateway/handlers/forwarding_proxy_test.go index 237068d0..7f5dc2ad 100644 --- a/gateway/handlers/forwarding_proxy_test.go +++ b/gateway/handlers/forwarding_proxy_test.go @@ -10,6 +10,8 @@ import ( "net/http" "net/url" "testing" + + "github.com/openfaas/faas/gateway/pkg/middleware" ) func Test_buildUpstreamRequest_Body_Method_Query(t *testing.T) { @@ -170,7 +172,7 @@ func Test_getServiceName(t *testing.T) { t.Fatal(err) } - service := getServiceName(u.Path) + service := middleware.GetServiceName(u.Path) if service != s.serviceName { t.Fatalf("Incorrect service name - want: %s, got: %s", s.serviceName, service) } @@ -195,7 +197,7 @@ func Test_buildUpstreamRequest_WithPathNoQuery(t *testing.T) { t.Fail() } - transformer := FunctionPrefixTrimmingURLPathTransformer{} + transformer := middleware.FunctionPrefixTrimmingURLPathTransformer{} transformedPath := transformer.Transform(request) wantTransformedPath := functionPath @@ -251,7 +253,7 @@ func Test_buildUpstreamRequest_WithNoPathNoQuery(t *testing.T) { t.Fail() } - transformer := FunctionPrefixTrimmingURLPathTransformer{} + transformer := middleware.FunctionPrefixTrimmingURLPathTransformer{} transformedPath := transformer.Transform(request) wantTransformedPath := "/" @@ -305,7 +307,7 @@ func Test_buildUpstreamRequest_WithPathAndQuery(t *testing.T) { t.Fail() } - transformer := FunctionPrefixTrimmingURLPathTransformer{} + transformer := middleware.FunctionPrefixTrimmingURLPathTransformer{} transformedPath := transformer.Transform(request) wantTransformedPath := functionPath diff --git a/gateway/handlers/namespaces_test.go b/gateway/handlers/namespaces_test.go index 56b3c4dd..ba2d23eb 100644 --- a/gateway/handlers/namespaces_test.go +++ b/gateway/handlers/namespaces_test.go @@ -3,10 +3,13 @@ package handlers -import "testing" +import ( + "github.com/openfaas/faas/gateway/pkg/middleware" + "testing" +) func Test_getNamespace_Default(t *testing.T) { - root, ns := getNamespace("openfaas-fn", "figlet.openfaas-fn") + root, ns := middleware.GetNamespace("openfaas-fn", "figlet.openfaas-fn") wantRoot := "figlet" wantNs := "openfaas-fn" @@ -19,7 +22,7 @@ func Test_getNamespace_Default(t *testing.T) { } func Test_getNamespace_Override(t *testing.T) { - root, ns := getNamespace("fn", "figlet.fn") + root, ns := middleware.GetNamespace("fn", "figlet.fn") wantRoot := "figlet" wantNs := "fn" @@ -32,7 +35,7 @@ func Test_getNamespace_Override(t *testing.T) { } func Test_getNamespace_Empty(t *testing.T) { - root, ns := getNamespace("", "figlet") + root, ns := middleware.GetNamespace("", "figlet") wantRoot := "figlet" wantNs := "" diff --git a/gateway/handlers/notifiers.go b/gateway/handlers/notifiers.go index d3e7a4ae..4795b90f 100644 --- a/gateway/handlers/notifiers.go +++ b/gateway/handlers/notifiers.go @@ -8,6 +8,7 @@ import ( "time" "github.com/openfaas/faas/gateway/metrics" + "github.com/openfaas/faas/gateway/pkg/middleware" "github.com/prometheus/client_golang/prometheus" ) @@ -49,7 +50,7 @@ type PrometheusFunctionNotifier struct { // Notify records metrics in Prometheus func (p PrometheusFunctionNotifier) Notify(method string, URL string, originalURL string, statusCode int, event string, duration time.Duration) { - serviceName := getServiceName(originalURL) + serviceName := middleware.GetServiceName(originalURL) if len(p.FunctionNamespace) > 0 { if !strings.Contains(serviceName, ".") { serviceName = fmt.Sprintf("%s.%s", serviceName, p.FunctionNamespace) @@ -74,24 +75,6 @@ func (p PrometheusFunctionNotifier) Notify(method string, URL string, originalUR } -func getServiceName(urlValue string) string { - var serviceName string - forward := "/function/" - if strings.HasPrefix(urlValue, forward) { - // With a path like `/function/xyz/rest/of/path?q=a`, the service - // name we wish to locate is just the `xyz` portion. With a positive - // match on the regex below, it will return a three-element slice. - // The item at index `0` is the same as `urlValue`, at `1` - // will be the service name we need, and at `2` the rest of the path. - matcher := functionMatcher.Copy() - matches := matcher.FindStringSubmatch(urlValue) - if len(matches) == hasPathCount { - serviceName = matches[nameIndex] - } - } - return strings.Trim(serviceName, "/") -} - // LoggingNotifier notifies a log about a request type LoggingNotifier struct { } diff --git a/gateway/handlers/probe_handler.go b/gateway/handlers/probe_handler.go new file mode 100644 index 00000000..47f83c8e --- /dev/null +++ b/gateway/handlers/probe_handler.go @@ -0,0 +1,45 @@ +package handlers + +import ( + "fmt" + "net/http" + + "golang.org/x/sync/singleflight" + + "github.com/openfaas/faas/gateway/pkg/middleware" + "github.com/openfaas/faas/gateway/probing" +) + +func MakeProbeHandler(prober probing.FunctionProber, cache probing.ProbeCacher, resolver middleware.BaseURLResolver, next http.HandlerFunc, defaultNamespace string) http.HandlerFunc { + + group := singleflight.Group{} + + return func(w http.ResponseWriter, r *http.Request) { + functionName, namespace := middleware.GetNamespace(defaultNamespace, middleware.GetServiceName(r.URL.String())) + + key := fmt.Sprintf("Probe-%s.%s", functionName, namespace) + res, _, _ := group.Do(key, func() (interface{}, error) { + + cached, hit := cache.Get(functionName, namespace) + var probeResult probing.FunctionProbeResult + if hit && cached != nil && cached.Available { + probeResult = *cached + } else { + probeResult = prober.Probe(functionName, namespace) + cache.Set(functionName, namespace, &probeResult) + } + + return probeResult, nil + }) + + fnRes := res.(probing.FunctionProbeResult) + + if !fnRes.Available { + http.Error(w, fmt.Sprintf("unable to probe function endpoint %s", fnRes.Error), + http.StatusServiceUnavailable) + return + } + + next(w, r) + } +} diff --git a/gateway/handlers/queue_proxy.go b/gateway/handlers/queue_proxy.go index 76d8341e..f3294c0e 100644 --- a/gateway/handlers/queue_proxy.go +++ b/gateway/handlers/queue_proxy.go @@ -14,6 +14,7 @@ import ( "github.com/gorilla/mux" ftypes "github.com/openfaas/faas-provider/types" "github.com/openfaas/faas/gateway/metrics" + "github.com/openfaas/faas/gateway/pkg/middleware" "github.com/openfaas/faas/gateway/scaling" ) @@ -21,7 +22,7 @@ import ( const queueAnnotation = "com.openfaas.queue" // MakeQueuedProxy accepts work onto a queue -func MakeQueuedProxy(metrics metrics.MetricOptions, queuer ftypes.RequestQueuer, pathTransformer URLPathTransformer, defaultNS string, functionQuery scaling.FunctionQuery) http.HandlerFunc { +func MakeQueuedProxy(metrics metrics.MetricOptions, queuer ftypes.RequestQueuer, pathTransformer middleware.URLPathTransformer, defaultNS string, functionQuery scaling.FunctionQuery) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.Body != nil { defer r.Body.Close() diff --git a/gateway/handlers/scaling.go b/gateway/handlers/scaling.go index 5ed3617f..69f1d3fa 100644 --- a/gateway/handlers/scaling.go +++ b/gateway/handlers/scaling.go @@ -7,18 +7,11 @@ import ( "fmt" "log" "net/http" - "strings" + "github.com/openfaas/faas/gateway/pkg/middleware" "github.com/openfaas/faas/gateway/scaling" ) -func getNamespace(defaultNamespace, fullName string) (string, string) { - if index := strings.LastIndex(fullName, "."); index > -1 { - return fullName[:index], fullName[index+1:] - } - return fullName, defaultNamespace -} - // MakeScalingHandler creates handler which can scale a function from // zero to N replica(s). After scaling the next http.HandlerFunc will // be called. If the function is not ready after the configured @@ -28,7 +21,7 @@ func MakeScalingHandler(next http.HandlerFunc, scaler scaling.FunctionScaler, co return func(w http.ResponseWriter, r *http.Request) { - functionName, namespace := getNamespace(defaultNamespace, getServiceName(r.URL.String())) + functionName, namespace := middleware.GetNamespace(defaultNamespace, middleware.GetServiceName(r.URL.String())) res := scaler.Scale(functionName, namespace) diff --git a/gateway/main.go b/gateway/main.go index 7661a034..428d342d 100644 --- a/gateway/main.go +++ b/gateway/main.go @@ -15,6 +15,7 @@ import ( "github.com/openfaas/faas/gateway/metrics" "github.com/openfaas/faas/gateway/pkg/middleware" "github.com/openfaas/faas/gateway/plugin" + "github.com/openfaas/faas/gateway/probing" "github.com/openfaas/faas/gateway/scaling" "github.com/openfaas/faas/gateway/types" "github.com/openfaas/faas/gateway/version" @@ -92,15 +93,16 @@ func main() { functionNotifiers := []handlers.HTTPNotifier{loggingNotifier, prometheusNotifier} forwardingNotifiers := []handlers.HTTPNotifier{loggingNotifier, prometheusServiceNotifier} + quietNotifier := []handlers.HTTPNotifier{prometheusServiceNotifier} - urlResolver := handlers.SingleHostBaseURLResolver{BaseURL: config.FunctionsProviderURL.String()} - var functionURLResolver handlers.BaseURLResolver - var functionURLTransformer handlers.URLPathTransformer - nilURLTransformer := handlers.TransparentURLPathTransformer{} - trimURLTransformer := handlers.FunctionPrefixTrimmingURLPathTransformer{} + urlResolver := middleware.SingleHostBaseURLResolver{BaseURL: config.FunctionsProviderURL.String()} + var functionURLResolver middleware.BaseURLResolver + var functionURLTransformer middleware.URLPathTransformer + nilURLTransformer := middleware.TransparentURLPathTransformer{} + trimURLTransformer := middleware.FunctionPrefixTrimmingURLPathTransformer{} if config.DirectFunctions { - functionURLResolver = handlers.FunctionAsHostBaseURLResolver{ + functionURLResolver = middleware.FunctionAsHostBaseURLResolver{ FunctionSuffix: config.DirectFunctionsSuffix, FunctionNamespace: config.Namespace, } @@ -118,6 +120,21 @@ func main() { decorateExternalAuth := handlers.MakeExternalAuthHandler + // externalServiceQuery is used to query metadata from the provider about a function + externalServiceQuery := plugin.NewExternalServiceQuery(*config.FunctionsProviderURL, serviceAuthInjector) + + scalingConfig := scaling.ScalingConfig{ + MaxPollCount: uint(1000), + SetScaleRetries: uint(20), + FunctionPollInterval: time.Millisecond * 100, + CacheExpiry: time.Millisecond * 250, // freshness of replica values before going stale + ServiceQuery: externalServiceQuery, + } + + // This cache can be used to query a function's annotations. + functionAnnotationCache := scaling.NewFunctionCache(scalingConfig.CacheExpiry) + cachedFunctionQuery := scaling.NewCachedFunctionQuery(functionAnnotationCache, externalServiceQuery) + faasHandlers.Proxy = handlers.MakeCallIDMiddleware( handlers.MakeForwardingProxyHandler(reverseProxy, functionNotifiers, functionURLResolver, functionURLTransformer, nil), ) @@ -133,27 +150,26 @@ func main() { faasHandlers.NamespaceListerHandler = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, nilURLTransformer, serviceAuthInjector) - externalServiceQuery := plugin.NewExternalServiceQuery(*config.FunctionsProviderURL, serviceAuthInjector) faasHandlers.Alert = handlers.MakeNotifierWrapper( handlers.MakeAlertHandler(externalServiceQuery, config.Namespace), - forwardingNotifiers, + quietNotifier, ) faasHandlers.LogProxyHandler = handlers.NewLogHandlerFunc(*config.LogsProviderURL, config.WriteTimeout) - scalingConfig := scaling.ScalingConfig{ - MaxPollCount: uint(1000), - SetScaleRetries: uint(20), - FunctionPollInterval: time.Millisecond * 100, - CacheExpiry: time.Millisecond * 250, // freshness of replica values before going stale - ServiceQuery: externalServiceQuery, + functionProxy := faasHandlers.Proxy + + if config.ProbeFunctions { + prober := probing.NewFunctionProber(cachedFunctionQuery, functionURLResolver) + // Default of 5 seconds between refreshing probes for function invocations + probeCache := probing.NewProbeCache(time.Second * 5) + functionProxy = handlers.MakeProbeHandler(prober, probeCache, functionURLResolver, functionProxy, config.Namespace) } - functionProxy := faasHandlers.Proxy if config.ScaleFromZero { scalingFunctionCache := scaling.NewFunctionCache(scalingConfig.CacheExpiry) scaler := scaling.NewFunctionScaler(scalingConfig, scalingFunctionCache) - functionProxy = handlers.MakeScalingHandler(faasHandlers.Proxy, scaler, scalingConfig, config.Namespace) + functionProxy = handlers.MakeScalingHandler(functionProxy, scaler, scalingConfig, config.Namespace) } if config.UseNATS() { @@ -168,11 +184,8 @@ func main() { log.Fatalln(queueErr) } - queueFunctionCache := scaling.NewFunctionCache(scalingConfig.CacheExpiry) - functionQuery := scaling.NewCachedFunctionQuery(queueFunctionCache, externalServiceQuery) - faasHandlers.QueuedProxy = handlers.MakeNotifierWrapper( - handlers.MakeCallIDMiddleware(handlers.MakeQueuedProxy(metricsOptions, natsQueue, trimURLTransformer, config.Namespace, functionQuery)), + handlers.MakeCallIDMiddleware(handlers.MakeQueuedProxy(metricsOptions, natsQueue, trimURLTransformer, config.Namespace, cachedFunctionQuery)), forwardingNotifiers, ) } diff --git a/gateway/pkg/middleware/baseurlresolver_test.go b/gateway/pkg/middleware/baseurlresolver_test.go new file mode 100644 index 00000000..0924060d --- /dev/null +++ b/gateway/pkg/middleware/baseurlresolver_test.go @@ -0,0 +1,155 @@ +// Copyright (c) OpenFaaS Author(s). All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +package middleware + +import ( + "fmt" + "log" + "net/http" + "net/url" + "strings" + "testing" +) + +func Test_SingleHostBaseURLResolver_BuildURL(t *testing.T) { + + newNamespace := "production-fn" + function := "figlet" + r := SingleHostBaseURLResolver{BaseURL: "http://faas-netes.openfaas:8080"} + + want := "http://faas-netes.openfaas:8080/function/figlet.production-fn/healthz" + + got := r.BuildURL(function, newNamespace, "/healthz", true) + if got != want { + t.Fatalf("r.URL failed, want: %s got: %s", want, got) + } +} + +func Test_SingleHostBaseURLResolver_BuildURL_DefaultNamespace(t *testing.T) { + + newNamespace := "openfaas-fn" + function := "figlet" + r := SingleHostBaseURLResolver{BaseURL: "http://faas-netes.openfaas:8080"} + + want := "http://faas-netes.openfaas:8080/function/figlet.openfaas-fn/_/health" + + got := r.BuildURL(function, newNamespace, "/_/health", true) + if got != want { + t.Fatalf("r.URL failed, want: %s got: %s", want, got) + } +} + +func TestSingleHostBaseURLResolver(t *testing.T) { + + urlVal, _ := url.Parse("http://upstream:8080/") + r := SingleHostBaseURLResolver{BaseURL: urlVal.String()} + + req, _ := http.NewRequest(http.MethodGet, "http://localhost/function/hello", nil) + + resolved := r.Resolve(req) + want := "http://upstream:8080" + if resolved != want { + t.Logf("r.Resolve failed, want: %s got: %s", want, resolved) + t.Fail() + } +} + +const watchdogPort = 8080 + +func TestURL_NonDefaultNamespaceWithPath(t *testing.T) { + suffix := "openfaas-fn.local.cluster.svc" + namespace := "openfaas-fn" + newNamespace := "production-fn" + function := "figlet" + r := FunctionAsHostBaseURLResolver{FunctionSuffix: suffix, FunctionNamespace: namespace} + + want := "http://figlet.production-fn.local.cluster.svc:8080/healthz" + + got := r.BuildURL(function, newNamespace, "/healthz", true) + if got != want { + t.Fatalf("r.URL failed, want: %s got: %s", want, got) + } +} + +func TestURL_NonDefaultNamespaceWithout(t *testing.T) { + suffix := "openfaas-fn.local.cluster.svc" + namespace := "openfaas-fn" + newNamespace := "production-fn" + function := "figlet" + r := FunctionAsHostBaseURLResolver{FunctionSuffix: suffix, FunctionNamespace: namespace} + + want := "http://figlet.production-fn.local.cluster.svc:8080" + + got := r.BuildURL(function, newNamespace, "", true) + if got != want { + t.Fatalf("r.URL failed, want: %s got: %s", want, got) + } +} + +func TestURL_DefaultNamespaceWithPath(t *testing.T) { + suffix := "openfaas-fn.local.cluster.svc" + namespace := "openfaas-fn" + newNamespace := "production-fn" + function := "figlet" + r := FunctionAsHostBaseURLResolver{FunctionSuffix: suffix, FunctionNamespace: namespace} + + want := "http://figlet.production-fn.local.cluster.svc:8080/_/health" + + got := r.BuildURL(function, newNamespace, "/_/health", true) + if got != want { + t.Fatalf("r.URL failed, want: %s got: %s", want, got) + } +} + +func TestFunctionAsHostBaseURLResolver_WithNamespaceOverride(t *testing.T) { + + suffix := "openfaas-fn.local.cluster.svc." + namespace := "openfaas-fn" + newNS := "production-fn" + + r := FunctionAsHostBaseURLResolver{FunctionSuffix: suffix, FunctionNamespace: namespace} + + req, _ := http.NewRequest(http.MethodGet, "http://localhost/function/hello."+newNS, nil) + + resolved := r.Resolve(req) + + newSuffix := strings.Replace(suffix, namespace, newNS, -1) + + want := fmt.Sprintf("http://hello.%s:%d", newSuffix, watchdogPort) + log.Println(want) + if resolved != want { + t.Logf("r.Resolve failed, want: %s got: %s", want, resolved) + t.Fail() + } +} + +func TestFunctionAsHostBaseURLResolver_WithSuffix(t *testing.T) { + suffix := "openfaas-fn.local.cluster.svc." + r := FunctionAsHostBaseURLResolver{FunctionSuffix: suffix} + + req, _ := http.NewRequest(http.MethodGet, "http://localhost/function/hello", nil) + + resolved := r.Resolve(req) + want := fmt.Sprintf("http://hello.%s:%d", suffix, watchdogPort) + log.Println(want) + if resolved != want { + t.Logf("r.Resolve failed, want: %s got: %s", want, resolved) + t.Fail() + } +} + +func TestFunctionAsHostBaseURLResolver_WithoutSuffix(t *testing.T) { + suffix := "" + r := FunctionAsHostBaseURLResolver{FunctionSuffix: suffix} + + req, _ := http.NewRequest(http.MethodGet, "http://localhost/function/hello", nil) + + resolved := r.Resolve(req) + want := fmt.Sprintf("http://hello%s:%d", suffix, watchdogPort) + + if resolved != want { + t.Logf("r.Resolve failed, want: %s got: %s", want, resolved) + t.Fail() + } +} diff --git a/gateway/handlers/function_prefix_trimming_url_path_transformer_test.go b/gateway/pkg/middleware/function_prefix_trimming_url_path_transformer_test.go similarity index 98% rename from gateway/handlers/function_prefix_trimming_url_path_transformer_test.go rename to gateway/pkg/middleware/function_prefix_trimming_url_path_transformer_test.go index f344ba99..6dfd644a 100644 --- a/gateway/handlers/function_prefix_trimming_url_path_transformer_test.go +++ b/gateway/pkg/middleware/function_prefix_trimming_url_path_transformer_test.go @@ -1,7 +1,7 @@ // Copyright (c) OpenFaaS Author(s). All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -package handlers +package middleware import ( "net/http" diff --git a/gateway/pkg/middleware/resolver.go b/gateway/pkg/middleware/resolver.go new file mode 100644 index 00000000..7fe2e50b --- /dev/null +++ b/gateway/pkg/middleware/resolver.go @@ -0,0 +1,156 @@ +package middleware + +import ( + "fmt" + "net/http" + "net/url" + "path" + "regexp" + "strings" +) + +// functionMatcher parses out the service name (group 1) and rest of path (group 2). +var functionMatcher = regexp.MustCompile("^/?(?:async-)?function/([^/?]+)([^?]*)") + +// Indices and meta-data for functionMatcher regex parts +const ( + hasPathCount = 3 + routeIndex = 0 // routeIndex corresponds to /function/ or /async-function/ + nameIndex = 1 // nameIndex is the function name + pathIndex = 2 // pathIndex is the path i.e. /employee/:id/ +) + +// BaseURLResolver URL resolver for upstream requests +type BaseURLResolver interface { + Resolve(r *http.Request) string + BuildURL(function, namespace, healthPath string, directFunctions bool) string +} + +// URLPathTransformer Transform the incoming URL path for upstream requests +type URLPathTransformer interface { + Transform(r *http.Request) string +} + +// SingleHostBaseURLResolver resolves URLs against a single BaseURL +type SingleHostBaseURLResolver struct { + BaseURL string +} + +func (s SingleHostBaseURLResolver) BuildURL(function, namespace, healthPath string, directFunctions bool) string { + u, _ := url.Parse(s.BaseURL) + + base := fmt.Sprintf("/function/%s.%s/", function, namespace) + + if len(healthPath) > 0 { + u.Path = path.Join(base, healthPath) + } else { + u.Path = base + } + + return u.String() +} + +// Resolve the base URL for a request +func (s SingleHostBaseURLResolver) Resolve(r *http.Request) string { + + baseURL := s.BaseURL + + if strings.HasSuffix(baseURL, "/") { + baseURL = baseURL[0 : len(baseURL)-1] + } + return baseURL +} + +// FunctionAsHostBaseURLResolver resolves URLs using a function from the URL as a host +type FunctionAsHostBaseURLResolver struct { + FunctionSuffix string + FunctionNamespace string +} + +// Resolve the base URL for a request +func (f FunctionAsHostBaseURLResolver) Resolve(r *http.Request) string { + svcName := GetServiceName(r.URL.Path) + + const watchdogPort = 8080 + var suffix string + + if len(f.FunctionSuffix) > 0 { + if index := strings.LastIndex(svcName, "."); index > -1 && len(svcName) > index+1 { + suffix = strings.Replace(f.FunctionSuffix, f.FunctionNamespace, "", -1) + } else { + suffix = "." + f.FunctionSuffix + } + } + + return fmt.Sprintf("http://%s%s:%d", svcName, suffix, watchdogPort) +} + +func (f FunctionAsHostBaseURLResolver) BuildURL(function, namespace, healthPath string, directFunctions bool) string { + svcName := function + + const watchdogPort = 8080 + var suffix string + + if len(f.FunctionSuffix) > 0 { + suffix = strings.Replace(f.FunctionSuffix, f.FunctionNamespace, namespace, 1) + } + + u, _ := url.Parse(fmt.Sprintf("http://%s.%s:%d", svcName, suffix, watchdogPort)) + if len(healthPath) > 0 { + u.Path = healthPath + } + + return u.String() +} + +// TransparentURLPathTransformer passes the requested URL path through untouched. +type TransparentURLPathTransformer struct { +} + +// Transform returns the URL path unchanged. +func (f TransparentURLPathTransformer) Transform(r *http.Request) string { + return r.URL.Path +} + +// FunctionPrefixTrimmingURLPathTransformer removes the "/function/servicename/" prefix from the URL path. +type FunctionPrefixTrimmingURLPathTransformer struct { +} + +// Transform removes the "/function/servicename/" prefix from the URL path. +func (f FunctionPrefixTrimmingURLPathTransformer) Transform(r *http.Request) string { + ret := r.URL.Path + + if ret != "" { + // When forwarding to a function, since the `/function/xyz` portion + // of a path like `/function/xyz/rest/of/path` is only used or needed + // by the Gateway, we want to trim it down to `/rest/of/path` for the + // upstream request. In the following regex, in the case of a match + // the r.URL.Path will be at `0`, the function name at `1` and the + // rest of the path (the part we are interested in) at `2`. + matcher := functionMatcher.Copy() + parts := matcher.FindStringSubmatch(ret) + if len(parts) == hasPathCount { + ret = parts[pathIndex] + } + } + + return ret +} + +func GetServiceName(urlValue string) string { + var serviceName string + forward := "/function/" + if strings.HasPrefix(urlValue, forward) { + // With a path like `/function/xyz/rest/of/path?q=a`, the service + // name we wish to locate is just the `xyz` portion. With a positive + // match on the regex below, it will return a three-element slice. + // The item at index `0` is the same as `urlValue`, at `1` + // will be the service name we need, and at `2` the rest of the path. + matcher := functionMatcher.Copy() + matches := matcher.FindStringSubmatch(urlValue) + if len(matches) == hasPathCount { + serviceName = matches[nameIndex] + } + } + return strings.Trim(serviceName, "/") +} diff --git a/gateway/pkg/middleware/service_name.go b/gateway/pkg/middleware/service_name.go new file mode 100644 index 00000000..a201923b --- /dev/null +++ b/gateway/pkg/middleware/service_name.go @@ -0,0 +1,10 @@ +package middleware + +import "strings" + +func GetNamespace(defaultNamespace, fullName string) (string, string) { + if index := strings.LastIndex(fullName, "."); index > -1 { + return fullName[:index], fullName[index+1:] + } + return fullName, defaultNamespace +} diff --git a/gateway/handlers/transparent_url_path_transformer_test.go b/gateway/pkg/middleware/transparent_url_path_transformer_test.go similarity index 97% rename from gateway/handlers/transparent_url_path_transformer_test.go rename to gateway/pkg/middleware/transparent_url_path_transformer_test.go index beb60ddf..47162f3f 100644 --- a/gateway/handlers/transparent_url_path_transformer_test.go +++ b/gateway/pkg/middleware/transparent_url_path_transformer_test.go @@ -1,7 +1,7 @@ // Copyright (c) OpenFaaS Author(s). All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -package handlers +package middleware import ( "net/http" diff --git a/gateway/plugin/external.go b/gateway/plugin/external.go index 14e92ee8..a23fa6ec 100644 --- a/gateway/plugin/external.go +++ b/gateway/plugin/external.go @@ -32,7 +32,7 @@ type ExternalServiceQuery struct { // NewExternalServiceQuery proxies service queries to external plugin via HTTP func NewExternalServiceQuery(externalURL url.URL, authInjector middleware.AuthInjector) scaling.ServiceQuery { - timeout := 5 * time.Second + timeout := 3 * time.Second proxyClient := http.Client{ Transport: &http.Transport{ @@ -82,35 +82,28 @@ func (s ExternalServiceQuery) GetReplicas(serviceName, serviceNamespace string) res, err := s.ProxyClient.Do(req) if err != nil { - log.Printf("Unable to connect to %s, error: %s", urlPath, err) + log.Println(urlPath, err) + return emptyServiceQueryResponse, err + + } + + var bytesOut []byte + if res.Body != nil { + bytesOut, _ = ioutil.ReadAll(res.Body) + defer res.Body.Close() + } + + if res.StatusCode == http.StatusOK { + if err := json.Unmarshal(bytesOut, &function); err != nil { + log.Printf("Unable to unmarshal: %q, %s", string(bytesOut), err) + return emptyServiceQueryResponse, err + } + + // log.Printf("GetReplicas [%s.%s] took: %fs", serviceName, serviceNamespace, time.Since(start).Seconds()) + } else { - - var body []byte - if res.Body != nil { - defer res.Body.Close() - body, _ = ioutil.ReadAll(res.Body) - } - - if res.StatusCode == http.StatusOK { - err = json.Unmarshal(body, &function) - if err != nil { - log.Printf("Unable to unmarshal %s, error: %s", string(body), err) - } - - log.Printf("GetReplicas [%s.%s] took: %fs", - serviceName, - serviceNamespace, - time.Since(start).Seconds()) - - } else { - log.Printf("GetReplicas [%s.%s] took: %fs, code: %d", - serviceName, - serviceNamespace, - time.Since(start).Seconds(), - res.StatusCode) - - return emptyServiceQueryResponse, fmt.Errorf("server returned non-200 status code (%d) for function, %s", res.StatusCode, serviceName) - } + log.Printf("GetReplicas [%s.%s] took: %fs, code: %d\n", serviceName, serviceNamespace, time.Since(start).Seconds(), res.StatusCode) + return emptyServiceQueryResponse, fmt.Errorf("server returned non-200 status code (%d) for function, %s, body: %s", res.StatusCode, serviceName, string(bytesOut)) } minReplicas := uint64(scaling.DefaultMinReplicas) @@ -128,7 +121,7 @@ func (s ExternalServiceQuery) GetReplicas(serviceName, serviceNamespace string) extractedScalingFactor := extractLabelValue(labels[scaling.ScalingFactorLabel], scalingFactor) targetLoad = extractLabelValue(labels[scaling.TargetLoadLabel], targetLoad) - if extractedScalingFactor > 0 && extractedScalingFactor <= 100 { + if extractedScalingFactor >= 0 && extractedScalingFactor <= 100 { scalingFactor = extractedScalingFactor } else { log.Printf("Bad Scaling Factor: %d, is not in range of [0 - 100]. Will fallback to %d", extractedScalingFactor, scalingFactor) diff --git a/gateway/probing/cache.go b/gateway/probing/cache.go new file mode 100644 index 00000000..d7d02ada --- /dev/null +++ b/gateway/probing/cache.go @@ -0,0 +1,58 @@ +// Copyright (c) OpenFaaS Author(s). All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +package probing + +import ( + "fmt" + "sync" + "time" +) + +// ProbeCacher queries functions and caches the results +type ProbeCacher interface { + Set(functionName, namespace string, result *FunctionProbeResult) + Get(functionName, namespace string) (result *FunctionProbeResult, hit bool) +} + +// ProbeCache provides a cache of Probe replica counts +type ProbeCache struct { + Cache map[string]*FunctionProbeResult + Expiry time.Duration + Sync sync.RWMutex +} + +// NewProbeCache creates a function cache to query function metadata +func NewProbeCache(cacheExpiry time.Duration) ProbeCacher { + return &ProbeCache{ + Cache: make(map[string]*FunctionProbeResult), + Expiry: cacheExpiry, + } +} + +// Set replica count for functionName +func (fc *ProbeCache) Set(functionName, namespace string, result *FunctionProbeResult) { + fc.Sync.Lock() + defer fc.Sync.Unlock() + + fc.Cache[functionName+"."+namespace] = result +} + +func (fc *ProbeCache) Get(functionName, namespace string) (*FunctionProbeResult, bool) { + + result := &FunctionProbeResult{ + Available: false, + Error: fmt.Errorf("unavailable in cache"), + } + + hit := false + fc.Sync.RLock() + defer fc.Sync.RUnlock() + + if val, exists := fc.Cache[functionName+"."+namespace]; exists { + hit = val.Expired(fc.Expiry) == false + result = val + } + + return result, hit +} diff --git a/gateway/probing/prober.go b/gateway/probing/prober.go new file mode 100644 index 00000000..b3f1aa3e --- /dev/null +++ b/gateway/probing/prober.go @@ -0,0 +1,116 @@ +// Copyright (c) OpenFaaS Author(s). All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +package probing + +import ( + "fmt" + "log" + "net/http" + "time" + + "github.com/openfaas/faas/gateway/pkg/middleware" + "github.com/openfaas/faas/gateway/scaling" + "github.com/openfaas/faas/gateway/types" +) + +// NewFunctionProber create a new scaler with the specified +// ScalingConfig +func NewFunctionProber(functionQuery scaling.FunctionQuery, resolver middleware.BaseURLResolver) FunctionProber { + // if directFunctions { + return &FunctionHTTPProber{ + Query: functionQuery, + Resolver: resolver, + } +} + +// FunctionHTTPProber probes a function's health endpoint +type FunctionHTTPProber struct { + Query scaling.FunctionQuery + Resolver middleware.BaseURLResolver + DirectFunctions bool +} + +type FunctionNonProber struct { +} + +func (f *FunctionNonProber) Probe(functionName, namespace string) FunctionProbeResult { + return FunctionProbeResult{ + Found: true, + Available: true, + } +} + +type FunctionProber interface { + Probe(functionName, namespace string) FunctionProbeResult +} + +// FunctionProbeResult holds the result of scaling from zero +type FunctionProbeResult struct { + Available bool + Error error + Found bool + Duration time.Duration + Updated time.Time +} + +// Expired find out whether the cache item has expired with +// the given expiry duration from when it was stored. +func (res *FunctionProbeResult) Expired(expiry time.Duration) bool { + return time.Now().After(res.Updated.Add(expiry)) +} + +// Scale scales a function from zero replicas to 1 or the value set in +// the minimum replicas metadata +func (f *FunctionHTTPProber) Probe(functionName, namespace string) FunctionProbeResult { + start := time.Now() + + cachedResponse, _ := f.Query.Get(functionName, namespace) + probePath := "/_/health" + + if cachedResponse.Annotations != nil { + if v, ok := (*cachedResponse.Annotations)["com.openfaas.http.path"]; ok && len(v) > 0 { + probePath = v + } + } + + maxCount := 10 + pollInterval := time.Millisecond * 50 + + err := types.Retry(func(attempt int) error { + u := f.Resolver.BuildURL(functionName, namespace, probePath, true) + + r, _ := http.NewRequest(http.MethodGet, u, nil) + r.Header.Set("User-Agent", "com.openfaas.gateway/probe") + + resp, err := http.DefaultClient.Do(r) + if err != nil { + return err + } + + log.Printf("[Probe] %s => %d", u, resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + return nil + } + return fmt.Errorf("failed with status: %s", resp.Status) + }, "Probe", maxCount, pollInterval) + + if err != nil { + return FunctionProbeResult{ + Error: err, + Available: false, + Found: true, + Duration: time.Since(start), + Updated: time.Now(), + } + } + + return FunctionProbeResult{ + Error: nil, + Available: true, + Found: true, + Duration: time.Since(start), + Updated: time.Now(), + } +} diff --git a/gateway/scaling/function_query.go b/gateway/scaling/function_query.go index ffb3b3c4..c049e9e2 100644 --- a/gateway/scaling/function_query.go +++ b/gateway/scaling/function_query.go @@ -49,8 +49,6 @@ func (c *CachedFunctionQuery) Get(fn string, ns string) (ServiceQueryResponse, e return c.serviceQuery.GetReplicas(fn, ns) }) - log.Printf("Result: %v %v", queryResponse, err) - if err != nil { return ServiceQueryResponse{}, err } diff --git a/gateway/scaling/function_scaler.go b/gateway/scaling/function_scaler.go index f59a5a1f..4ca0aa90 100644 --- a/gateway/scaling/function_scaler.go +++ b/gateway/scaling/function_scaler.go @@ -5,6 +5,7 @@ import ( "log" "time" + "github.com/openfaas/faas/gateway/types" "golang.org/x/sync/singleflight" ) @@ -80,7 +81,7 @@ func (f *FunctionScaler) Scale(functionName, namespace string) FunctionScaleResu minReplicas = queryResponse.MinReplicas } - scaleResult := backoff(func(attempt int) error { + scaleResult := types.Retry(func(attempt int) error { res, err, _ := f.SingleFlight.Do(getKey, func() (interface{}, error) { return f.Config.ServiceQuery.GetReplicas(functionName, namespace) @@ -114,7 +115,7 @@ func (f *FunctionScaler) Scale(functionName, namespace string) FunctionScaleResu return nil - }, int(f.Config.SetScaleRetries), f.Config.FunctionPollInterval) + }, "Scale", int(f.Config.SetScaleRetries), f.Config.FunctionPollInterval) if scaleResult != nil { return FunctionScaleResult{ @@ -171,23 +172,3 @@ func (f *FunctionScaler) Scale(functionName, namespace string) FunctionScaleResu Duration: time.Since(start), } } - -type routine func(attempt int) error - -func backoff(r routine, attempts int, interval time.Duration) error { - var err error - - for i := 0; i < attempts; i++ { - res := r(i) - if res != nil { - err = res - - log.Printf("Attempt: %d, had error: %s\n", i, res) - } else { - err = nil - break - } - time.Sleep(interval) - } - return err -} diff --git a/gateway/types/readconfig.go b/gateway/types/readconfig.go index d5fdac76..b0b2ff9f 100644 --- a/gateway/types/readconfig.go +++ b/gateway/types/readconfig.go @@ -175,6 +175,8 @@ func (ReadConfig) Read(hasEnv HasEnv) (*GatewayConfig, error) { } } + cfg.ProbeFunctions = parseBoolValue(hasEnv.Getenv("probe_functions")) + return &cfg, nil } @@ -243,6 +245,9 @@ type GatewayConfig struct { // Namespace for endpoints Namespace string + + // ProbeFunctions requires the gateway to probe the health endpoint of a function before invoking it + ProbeFunctions bool } // UseNATS Use NATSor not diff --git a/gateway/types/readconfig_test.go b/gateway/types/readconfig_test.go index e3364a59..e81a0276 100644 --- a/gateway/types/readconfig_test.go +++ b/gateway/types/readconfig_test.go @@ -137,6 +137,38 @@ func TestRead_DirectFunctionsOverride(t *testing.T) { } } +func TestRead_ProbeFunctions_Default(t *testing.T) { + defaults := NewEnvBucket() + readConfig := ReadConfig{} + defaults.Setenv("probe_functions", "") + + want := false + + config, _ := readConfig.Read(defaults) + + got := config.ProbeFunctions + if want != got { + t.Logf("ProbeFunctions want %v, but got %v", want, got) + t.Fail() + } +} + +func TestRead_ProbeFunctions_Enabled(t *testing.T) { + defaults := NewEnvBucket() + readConfig := ReadConfig{} + defaults.Setenv("probe_functions", "true") + + want := true + + config, _ := readConfig.Read(defaults) + + got := config.ProbeFunctions + if want != got { + t.Logf("ProbeFunctions want %v, but got %v", want, got) + t.Fail() + } +} + func TestRead_ScaleZeroDefaultAndOverride(t *testing.T) { defaults := NewEnvBucket() readConfig := ReadConfig{} diff --git a/gateway/types/retry.go b/gateway/types/retry.go new file mode 100644 index 00000000..f3825cd5 --- /dev/null +++ b/gateway/types/retry.go @@ -0,0 +1,25 @@ +package types + +import ( + "log" + "time" +) + +type routine func(attempt int) error + +func Retry(r routine, label string, attempts int, interval time.Duration) error { + var err error + + for i := 0; i < attempts; i++ { + res := r(i) + if res != nil { + err = res + log.Printf("[%s]: %d/%d, error: %s\n", label, i, attempts, res) + } else { + err = nil + break + } + time.Sleep(interval) + } + return err +} diff --git a/gateway/types/retry_test.go b/gateway/types/retry_test.go new file mode 100644 index 00000000..54bb31e3 --- /dev/null +++ b/gateway/types/retry_test.go @@ -0,0 +1,43 @@ +package types + +import ( + "fmt" + "testing" + "time" +) + +func Test_retry_early_success(t *testing.T) { + called := 0 + maxRetries := 10 + routine := func(i int) error { + + called++ + if called == 5 { + return nil + } + return fmt.Errorf("not called 5 times yet") + } + + Retry(routine, "test", maxRetries, time.Millisecond*5) + + want := 5 + if called != want { + t.Errorf("want: %d, got: %d", want, called) + } +} + +func Test_retry_until_max_attempts(t *testing.T) { + called := 0 + maxRetries := 10 + routine := func(i int) error { + called++ + return fmt.Errorf("unable to pass condition for routine") + } + + Retry(routine, "test", maxRetries, time.Millisecond*5) + + want := maxRetries + if called != want { + t.Errorf("want: %d, got: %d", want, called) + } +} diff --git a/gateway/vendor/github.com/cespare/xxhash/v2/go.mod b/gateway/vendor/github.com/cespare/xxhash/v2/go.mod deleted file mode 100644 index 49f67608..00000000 --- a/gateway/vendor/github.com/cespare/xxhash/v2/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/cespare/xxhash/v2 - -go 1.11 diff --git a/gateway/vendor/github.com/cespare/xxhash/v2/go.sum b/gateway/vendor/github.com/cespare/xxhash/v2/go.sum deleted file mode 100644 index e69de29b..00000000 diff --git a/gateway/vendor/github.com/gorilla/mux/go.mod b/gateway/vendor/github.com/gorilla/mux/go.mod deleted file mode 100644 index df170a39..00000000 --- a/gateway/vendor/github.com/gorilla/mux/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/gorilla/mux - -go 1.12 diff --git a/gateway/vendor/github.com/nats-io/nats.go/go.mod b/gateway/vendor/github.com/nats-io/nats.go/go.mod deleted file mode 100644 index 63faedf4..00000000 --- a/gateway/vendor/github.com/nats-io/nats.go/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/nats-io/nats.go - -go 1.16 - -require ( - github.com/nats-io/nkeys v0.3.0 - github.com/nats-io/nuid v1.0.1 -) diff --git a/gateway/vendor/github.com/nats-io/nats.go/go.sum b/gateway/vendor/github.com/nats-io/nats.go/go.sum deleted file mode 100644 index 2138ffb5..00000000 --- a/gateway/vendor/github.com/nats-io/nats.go/go.sum +++ /dev/null @@ -1,11 +0,0 @@ -github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= -github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= -github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as= -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/gateway/vendor/github.com/nats-io/nkeys/go.mod b/gateway/vendor/github.com/nats-io/nkeys/go.mod deleted file mode 100644 index 2384db3f..00000000 --- a/gateway/vendor/github.com/nats-io/nkeys/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/nats-io/nkeys - -go 1.16 - -require golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b diff --git a/gateway/vendor/github.com/nats-io/nkeys/go.sum b/gateway/vendor/github.com/nats-io/nkeys/go.sum deleted file mode 100644 index 42fec3ae..00000000 --- a/gateway/vendor/github.com/nats-io/nkeys/go.sum +++ /dev/null @@ -1,7 +0,0 @@ -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as= -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/gateway/vendor/github.com/nats-io/stan.go/go.mod b/gateway/vendor/github.com/nats-io/stan.go/go.mod deleted file mode 100644 index f2b270a4..00000000 --- a/gateway/vendor/github.com/nats-io/stan.go/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module github.com/nats-io/stan.go - -go 1.14 - -require ( - github.com/gogo/protobuf v1.3.2 - github.com/nats-io/nats.go v1.11.0 - github.com/nats-io/nuid v1.0.1 -) diff --git a/gateway/vendor/github.com/nats-io/stan.go/go.sum b/gateway/vendor/github.com/nats-io/stan.go/go.sum deleted file mode 100644 index 136ee84b..00000000 --- a/gateway/vendor/github.com/nats-io/stan.go/go.sum +++ /dev/null @@ -1,42 +0,0 @@ -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/nats-io/nats.go v1.11.0 h1:L263PZkrmkRJRJT2YHU8GwWWvEvmr9/LUKuJTXsF32k= -github.com/nats-io/nats.go v1.11.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= -github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= -github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= -github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as= -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/gateway/vendor/github.com/prometheus/procfs/go.mod b/gateway/vendor/github.com/prometheus/procfs/go.mod deleted file mode 100644 index ba6681f5..00000000 --- a/gateway/vendor/github.com/prometheus/procfs/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module github.com/prometheus/procfs - -go 1.13 - -require ( - github.com/google/go-cmp v0.5.4 - golang.org/x/sync v0.0.0-20201207232520-09787c993a3a - golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c -) diff --git a/gateway/vendor/github.com/prometheus/procfs/go.sum b/gateway/vendor/github.com/prometheus/procfs/go.sum deleted file mode 100644 index 7ceaf56b..00000000 --- a/gateway/vendor/github.com/prometheus/procfs/go.sum +++ /dev/null @@ -1,8 +0,0 @@ -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= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/gateway/vendor/go.uber.org/goleak/go.mod b/gateway/vendor/go.uber.org/goleak/go.mod deleted file mode 100644 index 742547ab..00000000 --- a/gateway/vendor/go.uber.org/goleak/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module go.uber.org/goleak - -go 1.13 - -require ( - github.com/kr/pretty v0.1.0 // indirect - github.com/stretchr/testify v1.4.0 - golang.org/x/lint v0.0.0-20190930215403-16217165b5de - golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect -) diff --git a/gateway/vendor/go.uber.org/goleak/go.sum b/gateway/vendor/go.uber.org/goleak/go.sum deleted file mode 100644 index 09b27d7e..00000000 --- a/gateway/vendor/go.uber.org/goleak/go.sum +++ /dev/null @@ -1,30 +0,0 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/gateway/vendor/golang.org/x/lint/go.mod b/gateway/vendor/golang.org/x/lint/go.mod deleted file mode 100644 index d5ba4dbf..00000000 --- a/gateway/vendor/golang.org/x/lint/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module golang.org/x/lint - -require golang.org/x/tools v0.0.0-20190311212946-11955173bddd diff --git a/gateway/vendor/golang.org/x/lint/go.sum b/gateway/vendor/golang.org/x/lint/go.sum deleted file mode 100644 index 7d0e2e61..00000000 --- a/gateway/vendor/golang.org/x/lint/go.sum +++ /dev/null @@ -1,6 +0,0 @@ -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= diff --git a/gateway/vendor/modules.txt b/gateway/vendor/modules.txt index 7140f0b0..13bf840f 100644 --- a/gateway/vendor/modules.txt +++ b/gateway/vendor/modules.txt @@ -1,84 +1,100 @@ # github.com/beorn7/perks v1.0.1 +## explicit; go 1.11 github.com/beorn7/perks/quantile # github.com/cespare/xxhash/v2 v2.1.1 +## explicit; go 1.11 github.com/cespare/xxhash/v2 # github.com/docker/distribution v2.8.1+incompatible ## explicit github.com/docker/distribution/uuid # github.com/gogo/protobuf v1.3.2 +## explicit; go 1.15 github.com/gogo/protobuf/gogoproto github.com/gogo/protobuf/proto github.com/gogo/protobuf/protoc-gen-gogo/descriptor # github.com/golang/protobuf v1.5.2 +## explicit; go 1.9 github.com/golang/protobuf/proto github.com/golang/protobuf/ptypes github.com/golang/protobuf/ptypes/any github.com/golang/protobuf/ptypes/duration github.com/golang/protobuf/ptypes/timestamp # github.com/gorilla/mux v1.8.0 -## explicit +## explicit; go 1.12 github.com/gorilla/mux # github.com/hashicorp/golang-lru v0.5.1 ## explicit # github.com/matttproud/golang_protobuf_extensions v1.0.1 +## explicit github.com/matttproud/golang_protobuf_extensions/pbutil # github.com/nats-io/nats.go v1.11.1-0.20210623165838-4b75fc59ae30 +## explicit; go 1.16 github.com/nats-io/nats.go github.com/nats-io/nats.go/encoders/builtin github.com/nats-io/nats.go/util # github.com/nats-io/nkeys v0.3.0 +## explicit; go 1.16 github.com/nats-io/nkeys # github.com/nats-io/nuid v1.0.1 +## explicit github.com/nats-io/nuid # github.com/nats-io/stan.go v0.9.0 +## explicit; go 1.14 github.com/nats-io/stan.go github.com/nats-io/stan.go/pb # github.com/openfaas/faas-provider v0.18.7 -## explicit +## explicit; go 1.16 github.com/openfaas/faas-provider/auth github.com/openfaas/faas-provider/types # github.com/openfaas/nats-queue-worker v0.0.0-20210726161954-ada9a31504c9 -## explicit +## explicit; go 1.16 github.com/openfaas/nats-queue-worker/handler github.com/openfaas/nats-queue-worker/nats # github.com/prometheus/client_golang v1.11.1 -## explicit +## explicit; go 1.13 github.com/prometheus/client_golang/prometheus github.com/prometheus/client_golang/prometheus/internal github.com/prometheus/client_golang/prometheus/promhttp # github.com/prometheus/client_model v0.2.0 -## explicit +## explicit; go 1.9 github.com/prometheus/client_model/go # github.com/prometheus/common v0.26.0 +## explicit; go 1.11 github.com/prometheus/common/expfmt github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg github.com/prometheus/common/model # github.com/prometheus/procfs v0.6.0 +## explicit; go 1.13 github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util # go.uber.org/goleak v1.1.10 -## explicit +## explicit; go 1.13 go.uber.org/goleak go.uber.org/goleak/internal/stack # golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e +## explicit; go 1.17 golang.org/x/crypto/ed25519 golang.org/x/crypto/ed25519/internal/edwards25519 # golang.org/x/lint v0.0.0-20190930215403-16217165b5de +## explicit golang.org/x/lint golang.org/x/lint/golint # golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f ## explicit golang.org/x/sync/singleflight # golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 +## explicit; go 1.17 golang.org/x/sys/internal/unsafeheader golang.org/x/sys/unix golang.org/x/sys/windows # golang.org/x/tools v0.0.0-20210106214847-113979e3529a +## explicit; go 1.12 golang.org/x/tools/go/ast/astutil golang.org/x/tools/go/gcexportdata golang.org/x/tools/go/internal/gcimporter # google.golang.org/protobuf v1.26.0 +## explicit; go 1.9 google.golang.org/protobuf/encoding/prototext google.golang.org/protobuf/encoding/protowire google.golang.org/protobuf/internal/descfmt diff --git a/go.mod b/go.mod index d656ef75..945ea1ad 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/openfaas/faas -go 1.15 +go 1.17 \ No newline at end of file