Feature for probing functions

Introduces a single-flight call to a function's health
endpoint to verify that it is registered with an Istio
sidecar (Envoy) before letting the invocation through.

Results are cached for 5 seconds, before a probe is
required again.

Tested without Istio, with probe_functions environment
variable set to true, I saw a probe execute in the logs.

Fixes: #1721 for Istio users.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alex@openfaas.com>
This commit is contained in:
Alex Ellis (OpenFaaS Ltd)
2022-06-29 10:09:01 +01:00
committed by Alex Ellis
parent 01841f605c
commit 88eea5f62e
43 changed files with 784 additions and 468 deletions

View File

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