From d8a5952cfe53e1c69a134bca6e93084ab25239d5 Mon Sep 17 00:00:00 2001 From: Lucas Roesler Date: Fri, 28 Jun 2019 19:57:20 +0200 Subject: [PATCH] Upgrade faas-provider to 0.9.2 Signed-off-by: Lucas Roesler --- gateway/Gopkg.lock | 6 +- .../openfaas/faas-provider/Gopkg.lock | 16 +- .../openfaas/faas-provider/README.md | 8 +- .../faas-provider/httputil/writers.go | 12 ++ .../faas-provider/logs/example/README.md | 2 + .../faas-provider/logs/example/main.go | 47 +++++ .../openfaas/faas-provider/logs/handler.go | 142 +++++++++++++ .../faas-provider/logs/handler_test.go | 191 ++++++++++++++++++ .../openfaas/faas-provider/logs/logs.go | 50 +++++ .../faas-provider/proxy/handler_test.go | 4 +- .../openfaas/faas-provider/proxy/proxy.go | 17 +- .../openfaas/faas-provider/serve.go | 2 + .../openfaas/faas-provider/types/config.go | 2 + 13 files changed, 478 insertions(+), 21 deletions(-) create mode 100644 gateway/vendor/github.com/openfaas/faas-provider/httputil/writers.go create mode 100644 gateway/vendor/github.com/openfaas/faas-provider/logs/example/README.md create mode 100644 gateway/vendor/github.com/openfaas/faas-provider/logs/example/main.go create mode 100644 gateway/vendor/github.com/openfaas/faas-provider/logs/handler.go create mode 100644 gateway/vendor/github.com/openfaas/faas-provider/logs/handler_test.go create mode 100644 gateway/vendor/github.com/openfaas/faas-provider/logs/logs.go diff --git a/gateway/Gopkg.lock b/gateway/Gopkg.lock index dff81766..f278e69b 100644 --- a/gateway/Gopkg.lock +++ b/gateway/Gopkg.lock @@ -93,12 +93,12 @@ version = "v1.0.0" [[projects]] - digest = "1:84708b04710afa61221f72ace164ce7fb6d26a7647b3a61d1af18d0e51f88d29" + digest = "1:d30085f782a2785c72cd5d4dda822aa615070ba745ea25e5a8a3661faa0ad980" name = "github.com/openfaas/faas-provider" packages = ["auth"] pruneopts = "" - revision = "376c26ef02007abb7cadbd550bb75df166764473" - version = "0.9.1" + revision = "0ca8ae603fee9736e011b81fbf02777d89a4ea85" + version = "0.9.2" [[projects]] digest = "1:c91d031a0f53699e18f204e8a8d360d400a34686a3bb34d82c63a53ff1d73cea" diff --git a/gateway/vendor/github.com/openfaas/faas-provider/Gopkg.lock b/gateway/vendor/github.com/openfaas/faas-provider/Gopkg.lock index 07862a29..ce1ac002 100644 --- a/gateway/vendor/github.com/openfaas/faas-provider/Gopkg.lock +++ b/gateway/vendor/github.com/openfaas/faas-provider/Gopkg.lock @@ -17,9 +17,23 @@ revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" version = "v1.6.2" +[[projects]] + digest = "1:9a1bb99a85e2ccddbc593aa0af084cdf6ea18ed081469eb90e7f6d0d303af6cd" + name = "go.uber.org/goleak" + packages = [ + ".", + "internal/stack", + ] + pruneopts = "UT" + revision = "1ac8aeca0a53163331564467638f6ffb639636bf" + version = "v0.10.0" + [solve-meta] analyzer-name = "dep" analyzer-version = 1 - input-imports = ["github.com/gorilla/mux"] + input-imports = [ + "github.com/gorilla/mux", + "go.uber.org/goleak", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/gateway/vendor/github.com/openfaas/faas-provider/README.md b/gateway/vendor/github.com/openfaas/faas-provider/README.md index 6d4a42a5..7d1f31a4 100644 --- a/gateway/vendor/github.com/openfaas/faas-provider/README.md +++ b/gateway/vendor/github.com/openfaas/faas-provider/README.md @@ -14,7 +14,7 @@ The faas-provider provides CRUD for functions and an invoke capability. If you c The following is used in OpenFaaS and recommended for those seeking to build their own back-ends: * License: MIT -* Language: Golang +* Language: Golang ### How to use this project @@ -25,6 +25,7 @@ For an example see the [server.go](https://github.com/openfaas/faas-netes/blob/m I.e.: ```go + timeout := 8 * time.Second bootstrapHandlers := bootTypes.FaaSHandlers{ FunctionProxy: handlers.MakeProxy(), DeleteHandler: handlers.MakeDeleteHandler(clientset), @@ -33,13 +34,14 @@ I.e.: ReplicaReader: handlers.MakeReplicaReader(clientset), ReplicaUpdater: handlers.MakeReplicaUpdater(clientset), InfoHandler: handlers.MakeInfoHandler(), + LogHandler: logs.NewLogHandlerFunc(requestor,timeout), } var port int port = 8080 bootstrapConfig := bootTypes.FaaSConfig{ - ReadTimeout: time.Second * 8, - WriteTimeout: time.Second * 8, + ReadTimeout: timeout, + WriteTimeout: timeout, TCPPort: &port, } diff --git a/gateway/vendor/github.com/openfaas/faas-provider/httputil/writers.go b/gateway/vendor/github.com/openfaas/faas-provider/httputil/writers.go new file mode 100644 index 00000000..c07e9935 --- /dev/null +++ b/gateway/vendor/github.com/openfaas/faas-provider/httputil/writers.go @@ -0,0 +1,12 @@ +package httputil + +import ( + "fmt" + "net/http" +) + +// Errorf sets the response status code and write formats the provided message as the +// response body +func Errorf(w http.ResponseWriter, statusCode int, msg string, args ...interface{}) { + http.Error(w, fmt.Sprintf(msg, args...), statusCode) +} diff --git a/gateway/vendor/github.com/openfaas/faas-provider/logs/example/README.md b/gateway/vendor/github.com/openfaas/faas-provider/logs/example/README.md new file mode 100644 index 00000000..8a6bf13b --- /dev/null +++ b/gateway/vendor/github.com/openfaas/faas-provider/logs/example/README.md @@ -0,0 +1,2 @@ +# Static Log Server Example +This example shows a very basic static log server. It will return the same 3 log messages for every request. diff --git a/gateway/vendor/github.com/openfaas/faas-provider/logs/example/main.go b/gateway/vendor/github.com/openfaas/faas-provider/logs/example/main.go new file mode 100644 index 00000000..03860366 --- /dev/null +++ b/gateway/vendor/github.com/openfaas/faas-provider/logs/example/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "context" + "net/http" + "time" + + "github.com/openfaas/faas-provider/logs" +) + +// staticLogRequestor implements the logs Requestor returning a static stream of logs +type staticLogRequestor struct { + logs []string +} + +func (s staticLogRequestor) Query(ctx context.Context, r logs.Request) (<-chan logs.Message, error) { + resp := make(chan logs.Message, len(s.logs)) + + // A real implementation would possibly run a query to their log storage here, if it returns a + // channel, and pass that channel to the go routine below instead of ranging over `s.logs` + // If the log storage backend client does not return a channel, the query would need to + // occur at the beginning of the goroutine below ... or a separate goroutine + + go func() { + for _, m := range s.logs { + // always watch the ctx to timeout/cancel/finish + if ctx.Err() != nil { + return + } + + resp <- logs.Message{ + Name: r.Name, + Instance: "fake", + Timestamp: time.Now(), + Text: m, + } + } + }() + + return resp, nil +} + +func main() { + requestor := staticLogRequestor{logs: []string{"msg1", "msg2", "something interesting"}} + http.HandleFunc("/system/logs", logs.NewLogHandlerFunc(requestor, 10*time.Second)) + http.ListenAndServe(":80", nil) +} diff --git a/gateway/vendor/github.com/openfaas/faas-provider/logs/handler.go b/gateway/vendor/github.com/openfaas/faas-provider/logs/handler.go new file mode 100644 index 00000000..1a1cc35d --- /dev/null +++ b/gateway/vendor/github.com/openfaas/faas-provider/logs/handler.go @@ -0,0 +1,142 @@ +package logs + +import ( + "context" + "encoding/json" + "log" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/openfaas/faas-provider/httputil" +) + +// Requester submits queries the logging system. +// This will be passed to the log handler constructor. +type Requester interface { + // Query submits a log request to the actual logging system. + Query(context.Context, Request) (<-chan Message, error) +} + +// NewLogHandlerFunc creates an http HandlerFunc from the supplied log Requestor. +func NewLogHandlerFunc(requestor Requester, timeout time.Duration) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if r.Body != nil { + defer r.Body.Close() + } + + cn, ok := w.(http.CloseNotifier) + if !ok { + log.Println("LogHandler: response is not a CloseNotifier, required for streaming response") + http.NotFound(w, r) + return + } + flusher, ok := w.(http.Flusher) + if !ok { + log.Println("LogHandler: response is not a Flusher, required for streaming response") + http.NotFound(w, r) + return + } + + logRequest, err := parseRequest(r) + if err != nil { + log.Printf("LogHandler: could not parse request %s", err) + httputil.Errorf(w, http.StatusUnprocessableEntity, "could not parse the log request") + return + } + + ctx, cancelQuery := context.WithTimeout(r.Context(), timeout) + defer cancelQuery() + messages, err := requestor.Query(ctx, logRequest) + if err != nil { + // add smarter error handling here + httputil.Errorf(w, http.StatusInternalServerError, "function log request failed") + return + } + + // Send the initial headers saying we're gonna stream the response. + w.Header().Set("Connection", "Keep-Alive") + w.Header().Set("Transfer-Encoding", "chunked") + w.Header().Set(http.CanonicalHeaderKey("Content-Type"), "application/x-ndjson") + w.WriteHeader(http.StatusOK) + flusher.Flush() + + // ensure that we always try to send the closing chunk, not the inverted order due to how + // the defer stack works. We need two flush statements to ensure that the empty slice is + // sent as its own chunk + defer flusher.Flush() + defer w.Write([]byte{}) + defer flusher.Flush() + + jsonEncoder := json.NewEncoder(w) + for messages != nil { + select { + case <-cn.CloseNotify(): + log.Println("LogHandler: client stopped listening") + return + case msg, ok := <-messages: + if !ok { + log.Println("LogHandler: end of log stream") + messages = nil + return + } + + // serialize and write the msg to the http ResponseWriter + err := jsonEncoder.Encode(msg) + if err != nil { + // can't actually write the status header here so we should json serialize an error + // and return that because we have already sent the content type and status code + log.Printf("LogHandler: failed to serialize log message: '%s'\n", msg.String()) + log.Println(err.Error()) + // write json error message here ? + jsonEncoder.Encode(Message{Text: "failed to serialize log message"}) + flusher.Flush() + return + } + + flusher.Flush() + } + } + + return + } +} + +// parseRequest extracts the logRequest from the GET variables or from the POST body +func parseRequest(r *http.Request) (logRequest Request, err error) { + query := r.URL.Query() + logRequest.Name = getValue(query, "name") + logRequest.Instance = getValue(query, "instance") + tailStr := getValue(query, "tail") + if tailStr != "" { + logRequest.Tail, err = strconv.Atoi(tailStr) + if err != nil { + return logRequest, err + } + } + // ignore error because it will default to false if we can't parse it + logRequest.Follow, _ = strconv.ParseBool(getValue(query, "follow")) + + sinceStr := getValue(query, "since") + if sinceStr != "" { + since, err := time.Parse(time.RFC3339, sinceStr) + logRequest.Since = &since + if err != nil { + return logRequest, err + } + } + + return logRequest, nil +} + +// getValue returns the value for the given key. If the key has more than one value, it returns the +// last value. if the value does not exist, it returns the empty string. +func getValue(queryValues url.Values, name string) string { + values := queryValues[name] + if len(values) == 0 { + return "" + } + + return values[len(values)-1] +} diff --git a/gateway/vendor/github.com/openfaas/faas-provider/logs/handler_test.go b/gateway/vendor/github.com/openfaas/faas-provider/logs/handler_test.go new file mode 100644 index 00000000..5b9c2a24 --- /dev/null +++ b/gateway/vendor/github.com/openfaas/faas-provider/logs/handler_test.go @@ -0,0 +1,191 @@ +package logs + +import ( + "bytes" + "context" + "encoding/json" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + "time" + + "go.uber.org/goleak" +) + +var queryTimeout = 30 * time.Second + +func Test_logsHandlerDoesNotLeakGoroutinesWhenProviderClosesStream(t *testing.T) { + defer goleak.VerifyNoLeaks(t) + + msgs := []Message{ + Message{Name: "funcFoo", Text: "msg 0"}, + Message{Name: "funcFoo", Text: "msg 1"}, + } + + var expected bytes.Buffer + json.NewEncoder(&expected).Encode(msgs[0]) + json.NewEncoder(&expected).Encode(msgs[1]) + + querier := newFakeQueryRequester(msgs, nil) + logHandler := NewLogHandlerFunc(querier, queryTimeout) + testSrv := httptest.NewServer(http.HandlerFunc(logHandler)) + defer testSrv.Close() + + resp, err := http.Get(testSrv.URL + "?name=funcFoo") + if err != nil { + t.Fatalf("unexpected error sending log request: %s", err) + } + + querier.Close() + + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("unexpected error reading log response: %s", err) + } + + if string(body) != expected.String() { + t.Fatalf("expected log message %s, got: %s", expected.String(), body) + } +} + +func Test_logsHandlerDoesNotLeakGoroutinesWhenClientClosesConnection(t *testing.T) { + defer goleak.VerifyNoLeaks(t) + + msgs := []Message{ + Message{Name: "funcFoo", Text: "msg 0"}, + Message{Name: "funcFoo", Text: "msg 1"}, + } + + querier := newFakeQueryRequester(msgs, nil) + logHandler := NewLogHandlerFunc(querier, queryTimeout) + testSrv := httptest.NewServer(http.HandlerFunc(logHandler)) + defer testSrv.Close() + + reqContext, cancel := context.WithCancel(context.Background()) + req, _ := http.NewRequest(http.MethodGet, testSrv.URL+"?name=funcFoo", nil) + + req = req.WithContext(reqContext) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("unexpected error sending log request: %s", err) + } + + go func() { + defer resp.Body.Close() + _, err := ioutil.ReadAll(resp.Body) + if err != context.Canceled { + t.Fatalf("unexpected error reading log response: %s", err) + } + }() + cancel() +} + +func Test_GETRequestParsing(t *testing.T) { + sinceTime, _ := time.Parse(time.RFC3339, "2019-02-16T09:10:06+00:00") + scenarios := []struct { + name string + rawQueryStr string + err string + expectedRequest Request + }{ + { + name: "empty query creates an empty request", + rawQueryStr: "", + err: "", + expectedRequest: Request{}, + }, + { + name: "name only query", + rawQueryStr: "name=foobar", + err: "", + expectedRequest: Request{Name: "foobar"}, + }, + { + name: "name only query", + rawQueryStr: "name=foobar", + err: "", + expectedRequest: Request{Name: "foobar"}, + }, + { + name: "multiple name values selects the last value", + rawQueryStr: "name=foobar&name=theactual name", + err: "", + expectedRequest: Request{Name: "theactual name"}, + }, + { + name: "valid request with every parameter", + rawQueryStr: "name=foobar&since=2019-02-16T09%3A10%3A06%2B00%3A00&tail=5&follow=true", + err: "", + expectedRequest: Request{ + Name: "foobar", + Since: &sinceTime, + Tail: 5, + Follow: true, + }, + }, + } + + req := httptest.NewRequest(http.MethodGet, "/", nil) + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + req.URL.RawQuery = s.rawQueryStr + logRequest, err := parseRequest(req) + equalError(t, s.err, err) + + if logRequest.String() != s.expectedRequest.String() { + t.Errorf("expected log request: %s, got: %s", s.expectedRequest, logRequest) + } + }) + } +} + +func equalError(t *testing.T, expected string, actual error) { + if expected == "" && actual == nil { + return + } + + if expected == "" && actual != nil { + t.Errorf("unexpected error: %s", actual.Error()) + return + } + + if actual.Error() != expected { + t.Errorf("expected error: %s got: %s", expected, actual.Error()) + } +} + +type fakeQueryRequester struct { + Logs []Message + err error + stream chan Message +} + +func (r fakeQueryRequester) Close() { + close(r.stream) + r.stream = nil +} + +func (r fakeQueryRequester) Query(context.Context, Request) (<-chan Message, error) { + if r.err != nil { + return nil, r.err + } + + for _, m := range r.Logs { + r.stream <- m + } + + return r.stream, nil +} + +func newFakeQueryRequester(l []Message, err error) fakeQueryRequester { + return fakeQueryRequester{ + Logs: l, + err: err, + stream: make(chan Message, len(l)), + } + +} diff --git a/gateway/vendor/github.com/openfaas/faas-provider/logs/logs.go b/gateway/vendor/github.com/openfaas/faas-provider/logs/logs.go new file mode 100644 index 00000000..6a48c50a --- /dev/null +++ b/gateway/vendor/github.com/openfaas/faas-provider/logs/logs.go @@ -0,0 +1,50 @@ +// Package logs provides the standard interface and handler for OpenFaaS providers to expose function logs. +// +// The package defines the Requester interface that OpenFaaS providers should implement and then expose using +// the predefined NewLogHandlerFunc. See the example folder for a minimal log provider implementation. +// +// The Requester is where the actual specific logic for connecting to and querying the log system should be implemented. +// +package logs + +import ( + "fmt" + "time" +) + +// Request is the query to return the function logs. +type Request struct { + // Name is the function name and is required + Name string `json:"name"` + // Instance is the optional container name, that allows you to request logs from a specific function instance + Instance string `json:"instance"` + // Since is the optional datetime value to start the logs from + Since *time.Time `json:"since"` + // Tail sets the maximum number of log messages to return, <=0 means unlimited + Tail int `json:"tail"` + // Follow is allows the user to request a stream of logs until the timeout + Follow bool `json:"follow"` +} + +// String implements that Stringer interface and prints the log Request in a consistent way that +// allows you to safely compare if two requests have the same value. +func (r Request) String() string { + return fmt.Sprintf("name:%s instance:%s since:%v tail:%d follow:%v", r.Name, r.Instance, r.Since, r.Tail, r.Follow) +} + +// Message is a specific log message from a function container log stream +type Message struct { + // Name is the function name + Name string `json:"name"` + // instance is the name/id of the specific function instance + Instance string `json:"instance"` + // Timestamp is the timestamp of when the log message was recorded + Timestamp time.Time `json:"timestamp"` + // Text is the raw log message content + Text string `json:"text"` +} + +// String implements the Stringer interface and allows for nice and simple string formatting of a log Message. +func (m Message) String() string { + return fmt.Sprintf("%s %s (%s) %s", m.Timestamp.String(), m.Name, m.Instance, m.Text) +} diff --git a/gateway/vendor/github.com/openfaas/faas-provider/proxy/handler_test.go b/gateway/vendor/github.com/openfaas/faas-provider/proxy/handler_test.go index 996847c9..c1e6b070 100644 --- a/gateway/vendor/github.com/openfaas/faas-provider/proxy/handler_test.go +++ b/gateway/vendor/github.com/openfaas/faas-provider/proxy/handler_test.go @@ -86,7 +86,7 @@ func Test_ProxyHandler_MissingFunctionNameError(t *testing.T) { t.Errorf("expected status code `%d`, got `%d`", http.StatusBadRequest, w.Code) } - respBody := w.Body.String() + respBody := strings.TrimSpace(w.Body.String()) if respBody != errMissingFunctionName { t.Errorf("expected error message `%s`, got `%s`", errMissingFunctionName, respBody) } @@ -109,7 +109,7 @@ func Test_ProxyHandler_ResolveError(t *testing.T) { t.Errorf("expected status code `%d`, got `%d`", http.StatusBadRequest, w.Code) } - respBody := w.Body.String() + respBody := strings.TrimSpace(w.Body.String()) if respBody != "Cannot find service: foo." { t.Errorf("expected error message `%s`, got `%s`", "Cannot find service: foo.", respBody) } diff --git a/gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy.go b/gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy.go index 069489b5..67b1afc6 100644 --- a/gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy.go +++ b/gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy.go @@ -21,7 +21,6 @@ package proxy import ( - "fmt" "io" "log" "net" @@ -30,6 +29,7 @@ import ( "time" "github.com/gorilla/mux" + "github.com/openfaas/faas-provider/httputils" ) const ( @@ -112,7 +112,7 @@ func proxyRequest(w http.ResponseWriter, originalReq *http.Request, proxyClient pathVars := mux.Vars(originalReq) functionName := pathVars["name"] if functionName == "" { - writeError(w, http.StatusBadRequest, errMissingFunctionName) + httputils.Errorf(w, http.StatusBadRequest, errMissingFunctionName) return } @@ -120,13 +120,13 @@ func proxyRequest(w http.ResponseWriter, originalReq *http.Request, proxyClient if resolveErr != nil { // TODO: Should record the 404/not found error in Prometheus. log.Printf("resolver error: cannot find %s: %s\n", functionName, resolveErr.Error()) - writeError(w, http.StatusNotFound, "Cannot find service: %s.", functionName) + httputils.Errorf(w, http.StatusNotFound, "Cannot find service: %s.", functionName) return } proxyReq, err := buildProxyRequest(originalReq, functionAddr, pathVars["params"]) if err != nil { - writeError(w, http.StatusInternalServerError, "Failed to resolve service: %s.", functionName) + httputils.Errorf(w, http.StatusInternalServerError, "Failed to resolve service: %s.", functionName) return } if proxyReq.Body != nil { @@ -140,7 +140,7 @@ func proxyRequest(w http.ResponseWriter, originalReq *http.Request, proxyClient if err != nil { log.Printf("error with proxy request to: %s, %s\n", proxyReq.URL.String(), err.Error()) - writeError(w, http.StatusInternalServerError, "Can't reach service for: %s.", functionName) + httputils.Errorf(w, http.StatusInternalServerError, "Can't reach service for: %s.", functionName) return } @@ -214,10 +214,3 @@ func getContentType(request http.Header, proxyResponse http.Header) (headerConte return headerContentType } - -// writeError sets the response status code and write formats the provided message as the -// response body -func writeError(w http.ResponseWriter, statusCode int, msg string, args ...interface{}) { - w.WriteHeader(statusCode) - w.Write([]byte(fmt.Sprintf(msg, args...))) -} diff --git a/gateway/vendor/github.com/openfaas/faas-provider/serve.go b/gateway/vendor/github.com/openfaas/faas-provider/serve.go index 3a9705d8..b3f6d1b0 100644 --- a/gateway/vendor/github.com/openfaas/faas-provider/serve.go +++ b/gateway/vendor/github.com/openfaas/faas-provider/serve.go @@ -46,6 +46,7 @@ func Serve(handlers *types.FaaSHandlers, config *types.FaaSConfig) { handlers.ReplicaUpdater = auth.DecorateWithBasicAuth(handlers.ReplicaUpdater, credentials) handlers.InfoHandler = auth.DecorateWithBasicAuth(handlers.InfoHandler, credentials) handlers.SecretHandler = auth.DecorateWithBasicAuth(handlers.SecretHandler, credentials) + handlers.LogHandler = auth.DecorateWithBasicAuth(handlers.LogHandler, credentials) } // System (auth) endpoints @@ -59,6 +60,7 @@ func Serve(handlers *types.FaaSHandlers, config *types.FaaSConfig) { r.HandleFunc("/system/info", handlers.InfoHandler).Methods("GET") r.HandleFunc("/system/secrets", handlers.SecretHandler).Methods(http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete) + r.HandleFunc("/system/logs", handlers.LogHandler).Methods(http.MethodGet) // Open endpoints r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}", handlers.FunctionProxy) diff --git a/gateway/vendor/github.com/openfaas/faas-provider/types/config.go b/gateway/vendor/github.com/openfaas/faas-provider/types/config.go index 75b686ab..d3ee709b 100644 --- a/gateway/vendor/github.com/openfaas/faas-provider/types/config.go +++ b/gateway/vendor/github.com/openfaas/faas-provider/types/config.go @@ -16,6 +16,8 @@ type FaaSHandlers struct { ReplicaReader http.HandlerFunc ReplicaUpdater http.HandlerFunc SecretHandler http.HandlerFunc + // LogHandler provides streaming json logs of functions + LogHandler http.HandlerFunc // Optional: Update an existing function UpdateHandler http.HandlerFunc