diff --git a/gateway/Gopkg.lock b/gateway/Gopkg.lock index 4b03d63b..9ba4427a 100644 --- a/gateway/Gopkg.lock +++ b/gateway/Gopkg.lock @@ -93,12 +93,12 @@ version = "v1.0.0" [[projects]] - digest = "1:6349b4be853e8be701de22e7d081c433447d185c0eeb8651d67bfc03c344f3fb" + digest = "1:84708b04710afa61221f72ace164ce7fb6d26a7647b3a61d1af18d0e51f88d29" name = "github.com/openfaas/faas-provider" packages = ["auth"] pruneopts = "" - revision = "220324e98f5db5aa61f02d1ab13f03e91310796c" - version = "0.8.1" + revision = "376c26ef02007abb7cadbd550bb75df166764473" + version = "0.9.1" [[projects]] digest = "1:c91d031a0f53699e18f204e8a8d360d400a34686a3bb34d82c63a53ff1d73cea" diff --git a/gateway/Gopkg.toml b/gateway/Gopkg.toml index a7f9c8a6..e5b14d96 100644 --- a/gateway/Gopkg.toml +++ b/gateway/Gopkg.toml @@ -26,4 +26,4 @@ ignored = ["github.com/openfaas/faas/gateway/queue"] [[constraint]] name = "github.com/openfaas/faas-provider" - version = "0.8.1" + version = "0.9.1" diff --git a/gateway/vendor/github.com/openfaas/faas-provider/.gitignore b/gateway/vendor/github.com/openfaas/faas-provider/.gitignore index bac5c3e8..c1546d29 100644 --- a/gateway/vendor/github.com/openfaas/faas-provider/.gitignore +++ b/gateway/vendor/github.com/openfaas/faas-provider/.gitignore @@ -13,4 +13,7 @@ # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 .glide/ +# Goland IDE +.idea + faas-backend diff --git a/gateway/vendor/github.com/openfaas/faas-provider/README.md b/gateway/vendor/github.com/openfaas/faas-provider/README.md index 691884cd..6d4a42a5 100644 --- a/gateway/vendor/github.com/openfaas/faas-provider/README.md +++ b/gateway/vendor/github.com/openfaas/faas-provider/README.md @@ -1,23 +1,30 @@ faas-provider ============== -This is a common template or interface for you to start building your own OpenFaaS backend. +This faas-provider can be used to write your own back-end for OpenFaaS. The Golang SDK can be vendored into your project so that you can provide a provider which is compliant and compatible with the OpenFaaS gateway. -Checkout the [backends guide here](https://github.com/openfaas/faas/blob/master/guide/backends.md) before starting. +![Conceptual diagram](docs/conceptual.png) -OpenFaaS projects use the MIT License and are written in Golang. We encourage the same for external / third-party providers. +The faas-provider provides CRUD for functions and an invoke capability. If you complete the required endpoints then you will be able to use your container orchestrator or back-end system with the existing OpenFaaS ecosystem and tooling. -### How to use this code +> See also: [backends guide](https://github.com/openfaas/faas/blob/master/guide/deprecated/backends.md) -We will setup all the standard HTTP routes for you, then start listening on a given TCP port - it should be 8080. +### Recommendations -Just implement the supplied routes. +The following is used in OpenFaaS and recommended for those seeking to build their own back-ends: -For an example checkout the [server.go](https://github.com/openfaas/faas-netes/blob/master/server.go) file in the [faas-netes](https://github.com/openfaas/faas-netes) Kubernetes backend. +* License: MIT +* Language: Golang + +### How to use this project + +All the required HTTP routes are configured automatically including a HTTP server on port 8080. Your task is to implement the supplied HTTP handler functions. + +For an example see the [server.go](https://github.com/openfaas/faas-netes/blob/master/server.go) file in the [faas-netes](https://github.com/openfaas/faas-netes) Kubernetes backend. I.e.: -```golang +```go bootstrapHandlers := bootTypes.FaaSHandlers{ FunctionProxy: handlers.MakeProxy(), DeleteHandler: handlers.MakeDeleteHandler(clientset), @@ -27,6 +34,7 @@ I.e.: ReplicaUpdater: handlers.MakeReplicaUpdater(clientset), InfoHandler: handlers.MakeInfoHandler(), } + var port int port = 8080 bootstrapConfig := bootTypes.FaaSConfig{ @@ -37,3 +45,7 @@ I.e.: bootstrap.Serve(&bootstrapHandlers, &bootstrapConfig) ``` + +### Need help? + +Join `#faas-provider` on [OpenFaaS Slack](https://docs.openfaas.com/community/) diff --git a/gateway/vendor/github.com/openfaas/faas-provider/auth/credentials.go b/gateway/vendor/github.com/openfaas/faas-provider/auth/credentials.go index 4f2ca34a..51f496ff 100644 --- a/gateway/vendor/github.com/openfaas/faas-provider/auth/credentials.go +++ b/gateway/vendor/github.com/openfaas/faas-provider/auth/credentials.go @@ -22,6 +22,10 @@ type ReadBasicAuth interface { type ReadBasicAuthFromDisk struct { SecretMountPath string + + UserFilename string + + PasswordFilename string } func (r *ReadBasicAuthFromDisk) Read() (*BasicAuthCredentials, error) { @@ -31,13 +35,23 @@ func (r *ReadBasicAuthFromDisk) Read() (*BasicAuthCredentials, error) { return nil, fmt.Errorf("invalid SecretMountPath specified for reading secrets") } - userPath := path.Join(r.SecretMountPath, "basic-auth-user") + userKey := "basic-auth-user" + if len(r.UserFilename) > 0 { + userKey = r.UserFilename + } + + passwordKey := "basic-auth-password" + if len(r.PasswordFilename) > 0 { + passwordKey = r.PasswordFilename + } + + userPath := path.Join(r.SecretMountPath, userKey) user, userErr := ioutil.ReadFile(userPath) if userErr != nil { return nil, fmt.Errorf("unable to load %s", userPath) } - userPassword := path.Join(r.SecretMountPath, "basic-auth-password") + userPassword := path.Join(r.SecretMountPath, passwordKey) password, passErr := ioutil.ReadFile(userPassword) if passErr != nil { return nil, fmt.Errorf("Unable to load %s", userPassword) diff --git a/gateway/vendor/github.com/openfaas/faas-provider/auth/credentials_test.go b/gateway/vendor/github.com/openfaas/faas-provider/auth/credentials_test.go new file mode 100644 index 00000000..5539176b --- /dev/null +++ b/gateway/vendor/github.com/openfaas/faas-provider/auth/credentials_test.go @@ -0,0 +1,64 @@ +// 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 auth + +import ( + "io/ioutil" + "os" + "path" + "testing" +) + +func Test_ReadFromCustomLocation_AndNames(t *testing.T) { + tmp := os.TempDir() + + userWant := "admin" + ioutil.WriteFile(path.Join(tmp, "user.txt"), []byte(userWant), 0700) + + passWant := "test1234" + ioutil.WriteFile(path.Join(tmp, "pass.txt"), []byte(passWant), 0700) + + reader := ReadBasicAuthFromDisk{ + SecretMountPath: tmp, + UserFilename: "user.txt", + PasswordFilename: "pass.txt", + } + + creds, err := reader.Read() + if err != nil { + t.Errorf("can't read secrets: %s", err.Error()) + } + + if creds.User != userWant { + t.Errorf("user, want: %s, got %s", userWant, creds.User) + } + if creds.Password != passWant { + t.Errorf("password, want: %s, got %s", passWant, creds.Password) + } +} + +func Test_ReadFromCustomLocation_DefaultNames(t *testing.T) { + tmp := os.TempDir() + userWant := "admin" + ioutil.WriteFile(path.Join(tmp, "basic-auth-user"), []byte(userWant), 0700) + + passWant := "test1234" + ioutil.WriteFile(path.Join(tmp, "basic-auth-password"), []byte(passWant), 0700) + + reader := ReadBasicAuthFromDisk{ + SecretMountPath: tmp, + } + + creds, err := reader.Read() + if err != nil { + t.Errorf("can't read secrets: %s", err.Error()) + } + + if creds.User != userWant { + t.Errorf("user, want: %s, got %s", userWant, creds.User) + } + if creds.Password != passWant { + t.Errorf("password, want: %s, got %s", passWant, creds.Password) + } +} diff --git a/gateway/vendor/github.com/openfaas/faas-provider/docs/conceptual.png b/gateway/vendor/github.com/openfaas/faas-provider/docs/conceptual.png new file mode 100644 index 00000000..9290c5e6 Binary files /dev/null and b/gateway/vendor/github.com/openfaas/faas-provider/docs/conceptual.png differ 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 86e40179..069489b5 100644 --- a/gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy.go +++ b/gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy.go @@ -129,7 +129,9 @@ func proxyRequest(w http.ResponseWriter, originalReq *http.Request, proxyClient writeError(w, http.StatusInternalServerError, "Failed to resolve service: %s.", functionName) return } - defer proxyReq.Body.Close() + if proxyReq.Body != nil { + defer proxyReq.Body.Close() + } start := time.Now() response, err := proxyClient.Do(proxyReq.WithContext(ctx)) @@ -148,7 +150,7 @@ func proxyRequest(w http.ResponseWriter, originalReq *http.Request, proxyClient copyHeaders(clientHeader, &response.Header) w.Header().Set("Content-Type", getContentType(response.Header, originalReq.Header)) - w.WriteHeader(http.StatusOK) + w.WriteHeader(response.StatusCode) io.Copy(w, response.Body) } diff --git a/gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy_test.go b/gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy_test.go index a68946b5..220f2e95 100644 --- a/gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy_test.go +++ b/gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy_test.go @@ -7,7 +7,9 @@ import ( "net/http" "net/http/httptest" "net/url" + "strings" "testing" + "time" "github.com/gorilla/mux" ) @@ -25,6 +27,58 @@ func testResolver(functionName string) (url.URL, error) { }, nil } +type mockResolver struct { + u *url.URL + err error +} + +func (m mockResolver) Resolve(name string) (url.URL, error) { + if m.u != nil { + return *m.u, m.err + } + return url.URL{}, m.err +} + +func Test_ProxyHandler_StatusCode(t *testing.T) { + + testcases := []int{200, 204, 400, 409, 422, 500, 503} + + for _, tc := range testcases { + t.Run(fmt.Sprintf("returns %d when upstream returns %d", tc, tc), func(t *testing.T) { + + // upstream represents the will be resolved and the function call then sent to + upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if tc > 399 { + http.Error(w, "upstream error", tc) + return + } + w.WriteHeader(tc) + })) + + u, err := url.Parse(upstream.URL) + proxyHandler := NewHandlerFunc(time.Second, mockResolver{u, err}) + + rr := httptest.NewRecorder() + req, err := http.NewRequest("GET", "", nil) + if err != nil { + t.Fatal(err) + } + + // we must set the function name URL variable to pass the validation in proxyRequest + req = mux.SetURLVars(req, map[string]string{"name": "foo"}) + proxyHandler.ServeHTTP(rr, req) + + if rr.Code != tc { + t.Fatalf("unexpected status code; got: %d, expected: %d", rr.Code, tc) + } + + if tc > 399 && strings.TrimSpace(rr.Body.String()) != "upstream error" { + t.Fatalf("unexpected response body, got: %s", rr.Body.String()) + } + }) + } +} + func Test_pathParsing(t *testing.T) { tt := []struct { name string diff --git a/gateway/vendor/github.com/openfaas/faas-provider/serve.go b/gateway/vendor/github.com/openfaas/faas-provider/serve.go index cf50d07e..3a9705d8 100644 --- a/gateway/vendor/github.com/openfaas/faas-provider/serve.go +++ b/gateway/vendor/github.com/openfaas/faas-provider/serve.go @@ -66,7 +66,7 @@ func Serve(handlers *types.FaaSHandlers, config *types.FaaSConfig) { r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}/{params:.*}", handlers.FunctionProxy) if config.EnableHealth { - r.HandleFunc("/healthz", handlers.Health).Methods("GET") + r.HandleFunc("/healthz", handlers.HealthHandler).Methods("GET") } readTimeout := config.ReadTimeout 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 fe446d31..75b686ab 100644 --- a/gateway/vendor/github.com/openfaas/faas-provider/types/config.go +++ b/gateway/vendor/github.com/openfaas/faas-provider/types/config.go @@ -19,7 +19,7 @@ type FaaSHandlers struct { // Optional: Update an existing function UpdateHandler http.HandlerFunc - Health http.HandlerFunc + HealthHandler http.HandlerFunc InfoHandler http.HandlerFunc }