mirror of
https://github.com/openfaas/faas.git
synced 2025-06-20 04:56:38 +00:00
Allow dot in function name
This patch enables the use-case for multiple namepsaces by allowing a dot to be used in the function name. dep has been run to update OpenFaaS projects and also to prune unused files. Tested by doing a build. Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
This commit is contained in:
committed by
Alex Ellis
parent
dc3c5fb9b3
commit
0a90125aba
1
gateway/vendor/github.com/openfaas/faas-provider/.DEREK.yml
generated
vendored
1
gateway/vendor/github.com/openfaas/faas-provider/.DEREK.yml
generated
vendored
@ -1 +0,0 @@
|
||||
redirect: https://raw.githubusercontent.com/openfaas/faas/master/.DEREK.yml
|
19
gateway/vendor/github.com/openfaas/faas-provider/.gitignore
generated
vendored
19
gateway/vendor/github.com/openfaas/faas-provider/.gitignore
generated
vendored
@ -1,19 +0,0 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
|
||||
# Goland IDE
|
||||
.idea
|
||||
|
||||
faas-backend
|
4
gateway/vendor/github.com/openfaas/faas-provider/.travis.yml
generated
vendored
4
gateway/vendor/github.com/openfaas/faas-provider/.travis.yml
generated
vendored
@ -1,4 +0,0 @@
|
||||
language: go
|
||||
go_import_path: github.com/openfaas/faas-provider
|
||||
script:
|
||||
- make test
|
25
gateway/vendor/github.com/openfaas/faas-provider/Dockerfile
generated
vendored
25
gateway/vendor/github.com/openfaas/faas-provider/Dockerfile
generated
vendored
@ -1,25 +0,0 @@
|
||||
FROM golang:1.10.4-alpine3.8
|
||||
|
||||
RUN mkdir -p /go/src/github.com/openfaas/faas-provider/
|
||||
|
||||
WORKDIR /go/src/github.com/openfaas/faas-provider
|
||||
|
||||
COPY vendor vendor
|
||||
COPY types types
|
||||
COPY auth auth
|
||||
COPY serve.go .
|
||||
|
||||
RUN go test ./auth/ -v \
|
||||
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o faas-provider .
|
||||
|
||||
FROM alpine:3.8
|
||||
RUN apk --no-cache add ca-certificates
|
||||
WORKDIR /root/
|
||||
|
||||
EXPOSE 8080
|
||||
ENV http_proxy ""
|
||||
ENV https_proxy ""
|
||||
|
||||
COPY --from=0 /go/src/github.com/openfaas/faas-provider/faas-provider .
|
||||
|
||||
CMD ["./faas-provider]
|
39
gateway/vendor/github.com/openfaas/faas-provider/Gopkg.lock
generated
vendored
39
gateway/vendor/github.com/openfaas/faas-provider/Gopkg.lock
generated
vendored
@ -1,39 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
digest = "1:160eabf7a69910fd74f29c692718bc2437c1c1c7d4c9dea9712357752a70e5df"
|
||||
name = "github.com/gorilla/context"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a"
|
||||
version = "v1.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:e73f5b0152105f18bc131fba127d9949305c8693f8a762588a82a48f61756f5f"
|
||||
name = "github.com/gorilla/mux"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
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",
|
||||
"go.uber.org/goleak",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
7
gateway/vendor/github.com/openfaas/faas-provider/Gopkg.toml
generated
vendored
7
gateway/vendor/github.com/openfaas/faas-provider/Gopkg.toml
generated
vendored
@ -1,7 +0,0 @@
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gorilla/mux"
|
||||
version = "1.6.2"
|
6
gateway/vendor/github.com/openfaas/faas-provider/Makefile
generated
vendored
6
gateway/vendor/github.com/openfaas/faas-provider/Makefile
generated
vendored
@ -1,6 +0,0 @@
|
||||
build:
|
||||
docker build -t faas-provider .
|
||||
|
||||
|
||||
test :
|
||||
go test -cover ./...
|
53
gateway/vendor/github.com/openfaas/faas-provider/README.md
generated
vendored
53
gateway/vendor/github.com/openfaas/faas-provider/README.md
generated
vendored
@ -1,53 +0,0 @@
|
||||
faas-provider
|
||||
==============
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||
> See also: [backends guide](https://github.com/openfaas/faas/blob/master/guide/deprecated/backends.md)
|
||||
|
||||
### Recommendations
|
||||
|
||||
The following is used in OpenFaaS and recommended for those seeking to build their own back-ends:
|
||||
|
||||
* 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.:
|
||||
|
||||
```go
|
||||
timeout := 8 * time.Second
|
||||
bootstrapHandlers := bootTypes.FaaSHandlers{
|
||||
FunctionProxy: handlers.MakeProxy(),
|
||||
DeleteHandler: handlers.MakeDeleteHandler(clientset),
|
||||
DeployHandler: handlers.MakeDeployHandler(clientset),
|
||||
FunctionReader: handlers.MakeFunctionReader(clientset),
|
||||
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: timeout,
|
||||
WriteTimeout: timeout,
|
||||
TCPPort: &port,
|
||||
}
|
||||
|
||||
bootstrap.Serve(&bootstrapHandlers, &bootstrapConfig)
|
||||
```
|
||||
|
||||
### Need help?
|
||||
|
||||
Join `#faas-provider` on [OpenFaaS Slack](https://docs.openfaas.com/community/)
|
66
gateway/vendor/github.com/openfaas/faas-provider/auth/basic_auth_test.go
generated
vendored
66
gateway/vendor/github.com/openfaas/faas-provider/auth/basic_auth_test.go
generated
vendored
@ -1,66 +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 auth
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_AuthWithValidPassword_Gives200(t *testing.T) {
|
||||
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
io.WriteString(w, "<html><body>Hello World!</body></html>")
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
wantUser := "admin"
|
||||
wantPassword := "password"
|
||||
r := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
|
||||
r.SetBasicAuth(wantUser, wantPassword)
|
||||
wantCredentials := &BasicAuthCredentials{
|
||||
User: wantUser,
|
||||
Password: wantPassword,
|
||||
}
|
||||
|
||||
decorated := DecorateWithBasicAuth(handler, wantCredentials)
|
||||
decorated.ServeHTTP(w, r)
|
||||
|
||||
wantCode := http.StatusOK
|
||||
|
||||
if w.Code != wantCode {
|
||||
t.Errorf("status code, want: %d, got: %d", wantCode, w.Code)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_AuthWithInvalidPassword_Gives403(t *testing.T) {
|
||||
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
io.WriteString(w, "<html><body>Hello World!</body></html>")
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
wantUser := "admin"
|
||||
wantPassword := "test"
|
||||
r := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
|
||||
r.SetBasicAuth(wantUser, wantPassword)
|
||||
|
||||
wantCredentials := &BasicAuthCredentials{
|
||||
User: wantUser,
|
||||
Password: "",
|
||||
}
|
||||
|
||||
decorated := DecorateWithBasicAuth(handler, wantCredentials)
|
||||
decorated.ServeHTTP(w, r)
|
||||
|
||||
wantCode := http.StatusUnauthorized
|
||||
if w.Code != wantCode {
|
||||
t.Errorf("status code, want: %d, got: %d", wantCode, w.Code)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
64
gateway/vendor/github.com/openfaas/faas-provider/auth/credentials_test.go
generated
vendored
64
gateway/vendor/github.com/openfaas/faas-provider/auth/credentials_test.go
generated
vendored
@ -1,64 +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 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)
|
||||
}
|
||||
}
|
BIN
gateway/vendor/github.com/openfaas/faas-provider/docs/conceptual.png
generated
vendored
BIN
gateway/vendor/github.com/openfaas/faas-provider/docs/conceptual.png
generated
vendored
Binary file not shown.
Before Width: | Height: | Size: 178 KiB |
12
gateway/vendor/github.com/openfaas/faas-provider/httputil/writers.go
generated
vendored
12
gateway/vendor/github.com/openfaas/faas-provider/httputil/writers.go
generated
vendored
@ -1,12 +0,0 @@
|
||||
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)
|
||||
}
|
2
gateway/vendor/github.com/openfaas/faas-provider/logs/example/README.md
generated
vendored
2
gateway/vendor/github.com/openfaas/faas-provider/logs/example/README.md
generated
vendored
@ -1,2 +0,0 @@
|
||||
# Static Log Server Example
|
||||
This example shows a very basic static log server. It will return the same 3 log messages for every request.
|
47
gateway/vendor/github.com/openfaas/faas-provider/logs/example/main.go
generated
vendored
47
gateway/vendor/github.com/openfaas/faas-provider/logs/example/main.go
generated
vendored
@ -1,47 +0,0 @@
|
||||
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)
|
||||
}
|
142
gateway/vendor/github.com/openfaas/faas-provider/logs/handler.go
generated
vendored
142
gateway/vendor/github.com/openfaas/faas-provider/logs/handler.go
generated
vendored
@ -1,142 +0,0 @@
|
||||
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]
|
||||
}
|
191
gateway/vendor/github.com/openfaas/faas-provider/logs/handler_test.go
generated
vendored
191
gateway/vendor/github.com/openfaas/faas-provider/logs/handler_test.go
generated
vendored
@ -1,191 +0,0 @@
|
||||
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)),
|
||||
}
|
||||
|
||||
}
|
50
gateway/vendor/github.com/openfaas/faas-provider/logs/logs.go
generated
vendored
50
gateway/vendor/github.com/openfaas/faas-provider/logs/logs.go
generated
vendored
@ -1,50 +0,0 @@
|
||||
// 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)
|
||||
}
|
134
gateway/vendor/github.com/openfaas/faas-provider/proxy/handler_test.go
generated
vendored
134
gateway/vendor/github.com/openfaas/faas-provider/proxy/handler_test.go
generated
vendored
@ -1,134 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type testBaseURLResolver struct {
|
||||
testServerBase string
|
||||
err error
|
||||
}
|
||||
|
||||
func (tr *testBaseURLResolver) Resolve(name string) (url.URL, error) {
|
||||
if tr.err != nil {
|
||||
return url.URL{}, tr.err
|
||||
}
|
||||
|
||||
return url.URL{
|
||||
Scheme: "http",
|
||||
Host: tr.testServerBase,
|
||||
}, nil
|
||||
}
|
||||
func Test_NewHandlerFunc_Panic(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("should panic if resolver is nil")
|
||||
}
|
||||
}()
|
||||
|
||||
NewHandlerFunc(time.Second, nil)
|
||||
}
|
||||
|
||||
func Test_NewHandlerFunc_NoPanic(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("should not panic if resolver is not nil")
|
||||
}
|
||||
}()
|
||||
|
||||
proxyFunc := NewHandlerFunc(time.Second, &testBaseURLResolver{})
|
||||
if proxyFunc == nil {
|
||||
t.Errorf("proxy handler func is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ProxyHandler_NonAllowedMethods(t *testing.T) {
|
||||
|
||||
proxyFunc := NewHandlerFunc(time.Second, &testBaseURLResolver{})
|
||||
|
||||
nonAllowedMethods := []string{
|
||||
http.MethodHead, http.MethodConnect, http.MethodOptions, http.MethodTrace,
|
||||
}
|
||||
|
||||
for _, method := range nonAllowedMethods {
|
||||
t.Run(method+" method is not allowed", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(method, "http://example.com/foo", nil)
|
||||
proxyFunc(w, req)
|
||||
resp := w.Result()
|
||||
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||
t.Errorf("expected status code `%d`, got `%d`", http.StatusMethodNotAllowed, resp.StatusCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ProxyHandler_MissingFunctionNameError(t *testing.T) {
|
||||
proxyFunc := NewHandlerFunc(time.Second, &testBaseURLResolver{"", nil})
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "http://example.com/foo", nil)
|
||||
req = mux.SetURLVars(req, map[string]string{"name": ""})
|
||||
|
||||
proxyFunc(w, req)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected status code `%d`, got `%d`", http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
respBody := strings.TrimSpace(w.Body.String())
|
||||
if respBody != errMissingFunctionName {
|
||||
t.Errorf("expected error message `%s`, got `%s`", errMissingFunctionName, respBody)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ProxyHandler_ResolveError(t *testing.T) {
|
||||
logs := &bytes.Buffer{}
|
||||
log.SetOutput(logs)
|
||||
|
||||
resolveErr := errors.New("can not find test service `foo`")
|
||||
proxyFunc := NewHandlerFunc(time.Second, &testBaseURLResolver{"", resolveErr})
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "http://example.com/foo", nil)
|
||||
req = mux.SetURLVars(req, map[string]string{"name": "foo"})
|
||||
|
||||
proxyFunc(w, req)
|
||||
|
||||
if w.Code != http.StatusNotFound {
|
||||
t.Errorf("expected status code `%d`, got `%d`", http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if !strings.Contains(logs.String(), resolveErr.Error()) {
|
||||
t.Errorf("expected logs to contain `%s`", resolveErr.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ProxyHandler_Proxy_Success(t *testing.T) {
|
||||
t.Skip("Test not implemented yet")
|
||||
// testFuncService := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// w.WriteHeader(http.StatusOK)
|
||||
// }))
|
||||
// proxyFunc := NewHandlerFunc(time.Second, &testBaseURLResolver{testFuncService.URL, nil})
|
||||
|
||||
// w := httptest.NewRecorder()
|
||||
// req := httptest.NewRequest("GET", "http://example.com/foo", nil)
|
||||
// req = mux.SetURLVars(req, map[string]string{"name": "foo"})
|
||||
|
||||
// proxyFunc(w, req)
|
||||
}
|
216
gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy.go
generated
vendored
216
gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy.go
generated
vendored
@ -1,216 +0,0 @@
|
||||
// Package proxy provides a default function invocation proxy method for OpenFaaS providers.
|
||||
//
|
||||
// The function proxy logic is used by the Gateway when `direct_functions` is set to false.
|
||||
// This means that the provider will direct call the function and return the results. This
|
||||
// involves resolving the function by name and then copying the result into the original HTTP
|
||||
// request.
|
||||
//
|
||||
// openfaas-provider has implemented a standard HTTP HandlerFunc that will handle setting
|
||||
// timeout values, parsing the request path, and copying the request/response correctly.
|
||||
// bootstrapHandlers := bootTypes.FaaSHandlers{
|
||||
// FunctionProxy: proxy.NewHandlerFunc(timeout, resolver),
|
||||
// DeleteHandler: handlers.MakeDeleteHandler(clientset),
|
||||
// DeployHandler: handlers.MakeDeployHandler(clientset),
|
||||
// FunctionReader: handlers.MakeFunctionReader(clientset),
|
||||
// ReplicaReader: handlers.MakeReplicaReader(clientset),
|
||||
// ReplicaUpdater: handlers.MakeReplicaUpdater(clientset),
|
||||
// InfoHandler: handlers.MakeInfoHandler(),
|
||||
// }
|
||||
//
|
||||
// proxy.NewHandlerFunc is optional, but does simplify the logic of your provider.
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/openfaas/faas-provider/httputil"
|
||||
)
|
||||
|
||||
const (
|
||||
watchdogPort = "8080"
|
||||
defaultContentType = "text/plain"
|
||||
errMissingFunctionName = "Please provide a valid route /function/function_name."
|
||||
)
|
||||
|
||||
// BaseURLResolver URL resolver for proxy requests
|
||||
//
|
||||
// The FaaS provider implementation is responsible for providing the resolver function implementation.
|
||||
// BaseURLResolver.Resolve will receive the function name and should return the URL of the
|
||||
// function service.
|
||||
type BaseURLResolver interface {
|
||||
Resolve(functionName string) (url.URL, error)
|
||||
}
|
||||
|
||||
// NewHandlerFunc creates a standard http.HandlerFunc to proxy function requests.
|
||||
// The returned http.HandlerFunc will ensure:
|
||||
//
|
||||
// - proper proxy request timeouts
|
||||
// - proxy requests for GET, POST, PATCH, PUT, and DELETE
|
||||
// - path parsing including support for extracing the function name, sub-paths, and query paremeters
|
||||
// - passing and setting the `X-Forwarded-Host` and `X-Forwarded-For` headers
|
||||
// - logging errors and proxy request timing to stdout
|
||||
//
|
||||
// Note that this will panic if `resolver` is nil.
|
||||
func NewHandlerFunc(timeout time.Duration, resolver BaseURLResolver) http.HandlerFunc {
|
||||
if resolver == nil {
|
||||
panic("NewHandlerFunc: empty proxy handler resolver, cannot be nil")
|
||||
}
|
||||
|
||||
proxyClient := http.Client{
|
||||
// these Transport values ensure that the http Client will eventually timeout and prevents
|
||||
// infinite retries. The default http.Client configure these timeouts. The specific
|
||||
// values tuned via performance testing/benchmarking
|
||||
//
|
||||
// Additional context can be found at
|
||||
// - https://medium.com/@nate510/don-t-use-go-s-default-http-client-4804cb19f779
|
||||
// - https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: timeout,
|
||||
KeepAlive: 1 * time.Second,
|
||||
}).DialContext,
|
||||
IdleConnTimeout: 120 * time.Millisecond,
|
||||
ExpectContinueTimeout: 1500 * time.Millisecond,
|
||||
},
|
||||
Timeout: timeout,
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Body != nil {
|
||||
defer r.Body.Close()
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodPost,
|
||||
http.MethodPut,
|
||||
http.MethodPatch,
|
||||
http.MethodDelete,
|
||||
http.MethodGet:
|
||||
|
||||
proxyRequest(w, r, proxyClient, resolver)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// proxyRequest handles the actual resolution of and then request to the function service.
|
||||
func proxyRequest(w http.ResponseWriter, originalReq *http.Request, proxyClient http.Client, resolver BaseURLResolver) {
|
||||
ctx := originalReq.Context()
|
||||
|
||||
pathVars := mux.Vars(originalReq)
|
||||
functionName := pathVars["name"]
|
||||
if functionName == "" {
|
||||
httputil.Errorf(w, http.StatusBadRequest, errMissingFunctionName)
|
||||
return
|
||||
}
|
||||
|
||||
functionAddr, resolveErr := resolver.Resolve(functionName)
|
||||
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())
|
||||
httputil.Errorf(w, http.StatusNotFound, "Cannot find service: %s.", functionName)
|
||||
return
|
||||
}
|
||||
|
||||
proxyReq, err := buildProxyRequest(originalReq, functionAddr, pathVars["params"])
|
||||
if err != nil {
|
||||
httputil.Errorf(w, http.StatusInternalServerError, "Failed to resolve service: %s.", functionName)
|
||||
return
|
||||
}
|
||||
if proxyReq.Body != nil {
|
||||
defer proxyReq.Body.Close()
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
response, err := proxyClient.Do(proxyReq.WithContext(ctx))
|
||||
seconds := time.Since(start)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("error with proxy request to: %s, %s\n", proxyReq.URL.String(), err.Error())
|
||||
|
||||
httputil.Errorf(w, http.StatusInternalServerError, "Can't reach service for: %s.", functionName)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("%s took %f seconds\n", functionName, seconds.Seconds())
|
||||
|
||||
clientHeader := w.Header()
|
||||
copyHeaders(clientHeader, &response.Header)
|
||||
w.Header().Set("Content-Type", getContentType(response.Header, originalReq.Header))
|
||||
|
||||
w.WriteHeader(response.StatusCode)
|
||||
io.Copy(w, response.Body)
|
||||
}
|
||||
|
||||
// buildProxyRequest creates a request object for the proxy request, it will ensure that
|
||||
// the original request headers are preserved as well as setting openfaas system headers
|
||||
func buildProxyRequest(originalReq *http.Request, baseURL url.URL, extraPath string) (*http.Request, error) {
|
||||
|
||||
host := baseURL.Host
|
||||
if baseURL.Port() == "" {
|
||||
host = baseURL.Host + ":" + watchdogPort
|
||||
}
|
||||
|
||||
url := url.URL{
|
||||
Scheme: baseURL.Scheme,
|
||||
Host: host,
|
||||
Path: extraPath,
|
||||
RawQuery: originalReq.URL.RawQuery,
|
||||
}
|
||||
|
||||
upstreamReq, err := http.NewRequest(originalReq.Method, url.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copyHeaders(upstreamReq.Header, &originalReq.Header)
|
||||
|
||||
if len(originalReq.Host) > 0 && upstreamReq.Header.Get("X-Forwarded-Host") == "" {
|
||||
upstreamReq.Header["X-Forwarded-Host"] = []string{originalReq.Host}
|
||||
}
|
||||
if upstreamReq.Header.Get("X-Forwarded-For") == "" {
|
||||
upstreamReq.Header["X-Forwarded-For"] = []string{originalReq.RemoteAddr}
|
||||
}
|
||||
|
||||
if originalReq.Body != nil {
|
||||
upstreamReq.Body = originalReq.Body
|
||||
}
|
||||
|
||||
return upstreamReq, nil
|
||||
}
|
||||
|
||||
// copyHeaders clones the header values from the source into the destination.
|
||||
func copyHeaders(destination http.Header, source *http.Header) {
|
||||
for k, v := range *source {
|
||||
vClone := make([]string, len(v))
|
||||
copy(vClone, v)
|
||||
destination[k] = vClone
|
||||
}
|
||||
}
|
||||
|
||||
// getContentType resolves the correct Content-Type for a proxied function.
|
||||
func getContentType(request http.Header, proxyResponse http.Header) (headerContentType string) {
|
||||
responseHeader := proxyResponse.Get("Content-Type")
|
||||
requestHeader := request.Get("Content-Type")
|
||||
|
||||
if len(responseHeader) > 0 {
|
||||
headerContentType = responseHeader
|
||||
} else if len(requestHeader) > 0 {
|
||||
headerContentType = requestHeader
|
||||
} else {
|
||||
headerContentType = defaultContentType
|
||||
}
|
||||
|
||||
return headerContentType
|
||||
}
|
439
gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy_test.go
generated
vendored
439
gateway/vendor/github.com/openfaas/faas-provider/proxy/proxy_test.go
generated
vendored
@ -1,439 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func varHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(fmt.Sprintf("name: %s params: %s", vars["name"], vars["params"])))
|
||||
}
|
||||
|
||||
func testResolver(functionName string) (url.URL, error) {
|
||||
return url.URL{
|
||||
Scheme: "http",
|
||||
Host: functionName,
|
||||
}, 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
|
||||
functionPath string
|
||||
functionName string
|
||||
extraPath string
|
||||
statusCode int
|
||||
}{
|
||||
{
|
||||
"simple_name_match",
|
||||
"/function/echo",
|
||||
"echo",
|
||||
"",
|
||||
200,
|
||||
},
|
||||
{
|
||||
"simple_name_match_with_trailing_slash",
|
||||
"/function/echo/",
|
||||
"echo",
|
||||
"",
|
||||
200,
|
||||
},
|
||||
{
|
||||
"name_match_with_additional_path_values",
|
||||
"/function/echo/subPath/extras",
|
||||
"echo",
|
||||
"subPath/extras",
|
||||
200,
|
||||
},
|
||||
{
|
||||
"name_match_with_additional_path_values_and_querystring",
|
||||
"/function/echo/subPath/extras?query=true",
|
||||
"echo",
|
||||
"subPath/extras",
|
||||
200,
|
||||
},
|
||||
{
|
||||
"not_found_if_no_name",
|
||||
"/function/",
|
||||
"",
|
||||
"",
|
||||
404,
|
||||
},
|
||||
}
|
||||
|
||||
// Need to create a router that we can pass the request through so that the vars will be added to the context
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}", varHandler)
|
||||
router.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}/", varHandler)
|
||||
router.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}/{params:.*}", varHandler)
|
||||
|
||||
for _, s := range tt {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
req, err := http.NewRequest("GET", s.functionPath, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
router.ServeHTTP(rr, req)
|
||||
if rr.Code != s.statusCode {
|
||||
t.Fatalf("unexpected status code; got: %d, expected: %d", rr.Code, s.statusCode)
|
||||
}
|
||||
|
||||
body := rr.Body.String()
|
||||
expectedBody := fmt.Sprintf("name: %s params: %s", s.functionName, s.extraPath)
|
||||
if s.statusCode == http.StatusOK && body != expectedBody {
|
||||
t.Fatalf("incorrect function name and path params; got: %s, expected: %s", body, expectedBody)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_buildProxyRequest_Body_Method_Query(t *testing.T) {
|
||||
srcBytes := []byte("hello world")
|
||||
|
||||
reader := bytes.NewReader(srcBytes)
|
||||
request, _ := http.NewRequest(http.MethodPost, "/?code=1", reader)
|
||||
request.Header.Set("X-Source", "unit-test")
|
||||
|
||||
if request.URL.RawQuery != "code=1" {
|
||||
t.Errorf("Query - want: %s, got: %s", "code=1", request.URL.RawQuery)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
funcURL, _ := testResolver("funcName")
|
||||
upstream, err := buildProxyRequest(request, funcURL, "")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if request.Method != upstream.Method {
|
||||
t.Errorf("Method - want: %s, got: %s", request.Method, upstream.Method)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
upstreamBytes, _ := ioutil.ReadAll(upstream.Body)
|
||||
|
||||
if string(upstreamBytes) != string(srcBytes) {
|
||||
t.Errorf("Body - want: %s, got: %s", string(upstreamBytes), string(srcBytes))
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if request.Header.Get("X-Source") != upstream.Header.Get("X-Source") {
|
||||
t.Errorf("Header X-Source - want: %s, got: %s", request.Header.Get("X-Source"), upstream.Header.Get("X-Source"))
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if request.URL.RawQuery != upstream.URL.RawQuery {
|
||||
t.Errorf("URL.RawQuery - want: %s, got: %s", request.URL.RawQuery, upstream.URL.RawQuery)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_buildProxyRequest_NoBody_GetMethod_NoQuery(t *testing.T) {
|
||||
request, _ := http.NewRequest(http.MethodGet, "/", nil)
|
||||
|
||||
funcURL, _ := testResolver("funcName")
|
||||
upstream, err := buildProxyRequest(request, funcURL, "")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if request.Method != upstream.Method {
|
||||
t.Errorf("Method - want: %s, got: %s", request.Method, upstream.Method)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if upstream.Body != nil {
|
||||
t.Errorf("Body - expected nil")
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if request.URL.RawQuery != upstream.URL.RawQuery {
|
||||
t.Errorf("URL.RawQuery - want: %s, got: %s", request.URL.RawQuery, upstream.URL.RawQuery)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_buildProxyRequest_HasXForwardedHostHeaderWhenSet(t *testing.T) {
|
||||
srcBytes := []byte("hello world")
|
||||
|
||||
reader := bytes.NewReader(srcBytes)
|
||||
request, err := http.NewRequest(http.MethodPost, "http://gateway/function?code=1", reader)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
funcURL, _ := testResolver("funcName")
|
||||
upstream, err := buildProxyRequest(request, funcURL, "/")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if request.Host != upstream.Header.Get("X-Forwarded-Host") {
|
||||
t.Errorf("Host - want: %s, got: %s", request.Host, upstream.Header.Get("X-Forwarded-Host"))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_buildProxyRequest_XForwardedHostHeader_Empty_WhenNotSet(t *testing.T) {
|
||||
srcBytes := []byte("hello world")
|
||||
|
||||
reader := bytes.NewReader(srcBytes)
|
||||
request, err := http.NewRequest(http.MethodPost, "/function", reader)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
funcURL, _ := testResolver("funcName")
|
||||
upstream, err := buildProxyRequest(request, funcURL, "/")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if request.Host != upstream.Header.Get("X-Forwarded-Host") {
|
||||
t.Errorf("Host - want: %s, got: %s", request.Host, upstream.Header.Get("X-Forwarded-Host"))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_buildProxyRequest_XForwardedHostHeader_WhenAlreadyPresent(t *testing.T) {
|
||||
srcBytes := []byte("hello world")
|
||||
headerValue := "test.openfaas.com"
|
||||
reader := bytes.NewReader(srcBytes)
|
||||
request, err := http.NewRequest(http.MethodPost, "/function/test", reader)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
request.Header.Set("X-Forwarded-Host", headerValue)
|
||||
funcURL, _ := testResolver("funcName")
|
||||
upstream, err := buildProxyRequest(request, funcURL, "/")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if upstream.Header.Get("X-Forwarded-Host") != headerValue {
|
||||
t.Errorf("X-Forwarded-Host - want: %s, got: %s", headerValue, upstream.Header.Get("X-Forwarded-Host"))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_buildProxyRequest_WithPathNoQuery(t *testing.T) {
|
||||
srcBytes := []byte("hello world")
|
||||
functionPath := "/employee/info/300"
|
||||
|
||||
requestPath := fmt.Sprintf("/function/xyz%s", functionPath)
|
||||
|
||||
reader := bytes.NewReader(srcBytes)
|
||||
request, _ := http.NewRequest(http.MethodPost, requestPath, reader)
|
||||
request.Header.Set("X-Source", "unit-test")
|
||||
|
||||
queryWant := ""
|
||||
if request.URL.RawQuery != queryWant {
|
||||
|
||||
t.Errorf("Query - want: %s, got: %s", queryWant, request.URL.RawQuery)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
funcURL, _ := testResolver("xyz")
|
||||
upstream, err := buildProxyRequest(request, funcURL, functionPath)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if request.Method != upstream.Method {
|
||||
t.Errorf("Method - want: %s, got: %s", request.Method, upstream.Method)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
upstreamBytes, _ := ioutil.ReadAll(upstream.Body)
|
||||
|
||||
if string(upstreamBytes) != string(srcBytes) {
|
||||
t.Errorf("Body - want: %s, got: %s", string(upstreamBytes), string(srcBytes))
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if request.Header.Get("X-Source") != upstream.Header.Get("X-Source") {
|
||||
t.Errorf("Header X-Source - want: %s, got: %s", request.Header.Get("X-Source"), upstream.Header.Get("X-Source"))
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if request.URL.RawQuery != upstream.URL.RawQuery {
|
||||
t.Errorf("URL.RawQuery - want: %s, got: %s", request.URL.RawQuery, upstream.URL.RawQuery)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if functionPath != upstream.URL.Path {
|
||||
t.Errorf("URL.Path - want: %s, got: %s", functionPath, upstream.URL.Path)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_buildProxyRequest_WithNoPathNoQuery(t *testing.T) {
|
||||
srcBytes := []byte("hello world")
|
||||
functionPath := "/"
|
||||
|
||||
requestPath := fmt.Sprintf("/function/xyz%s", functionPath)
|
||||
|
||||
reader := bytes.NewReader(srcBytes)
|
||||
request, _ := http.NewRequest(http.MethodPost, requestPath, reader)
|
||||
request.Header.Set("X-Source", "unit-test")
|
||||
|
||||
queryWant := ""
|
||||
if request.URL.RawQuery != queryWant {
|
||||
|
||||
t.Errorf("Query - want: %s, got: %s", queryWant, request.URL.RawQuery)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
funcURL, _ := testResolver("xyz")
|
||||
upstream, err := buildProxyRequest(request, funcURL, functionPath)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if request.Method != upstream.Method {
|
||||
t.Errorf("Method - want: %s, got: %s", request.Method, upstream.Method)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
upstreamBytes, _ := ioutil.ReadAll(upstream.Body)
|
||||
|
||||
if string(upstreamBytes) != string(srcBytes) {
|
||||
t.Errorf("Body - want: %s, got: %s", string(upstreamBytes), string(srcBytes))
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if request.Header.Get("X-Source") != upstream.Header.Get("X-Source") {
|
||||
t.Errorf("Header X-Source - want: %s, got: %s", request.Header.Get("X-Source"), upstream.Header.Get("X-Source"))
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if request.URL.RawQuery != upstream.URL.RawQuery {
|
||||
t.Errorf("URL.RawQuery - want: %s, got: %s", request.URL.RawQuery, upstream.URL.RawQuery)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if functionPath != upstream.URL.Path {
|
||||
t.Errorf("URL.Path - want: %s, got: %s", functionPath, upstream.URL.Path)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_buildProxyRequest_WithPathAndQuery(t *testing.T) {
|
||||
srcBytes := []byte("hello world")
|
||||
functionPath := "/employee/info/300"
|
||||
|
||||
requestPath := fmt.Sprintf("/function/xyz%s?code=1", functionPath)
|
||||
|
||||
reader := bytes.NewReader(srcBytes)
|
||||
request, _ := http.NewRequest(http.MethodPost, requestPath, reader)
|
||||
request.Header.Set("X-Source", "unit-test")
|
||||
|
||||
if request.URL.RawQuery != "code=1" {
|
||||
t.Errorf("Query - want: %s, got: %s", "code=1", request.URL.RawQuery)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
funcURL, _ := testResolver("xyz")
|
||||
upstream, err := buildProxyRequest(request, funcURL, functionPath)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if request.Method != upstream.Method {
|
||||
t.Errorf("Method - want: %s, got: %s", request.Method, upstream.Method)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
upstreamBytes, _ := ioutil.ReadAll(upstream.Body)
|
||||
|
||||
if string(upstreamBytes) != string(srcBytes) {
|
||||
t.Errorf("Body - want: %s, got: %s", string(upstreamBytes), string(srcBytes))
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if request.Header.Get("X-Source") != upstream.Header.Get("X-Source") {
|
||||
t.Errorf("Header X-Source - want: %s, got: %s", request.Header.Get("X-Source"), upstream.Header.Get("X-Source"))
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if request.URL.RawQuery != upstream.URL.RawQuery {
|
||||
t.Errorf("URL.RawQuery - want: %s, got: %s", request.URL.RawQuery, upstream.URL.RawQuery)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if functionPath != upstream.URL.Path {
|
||||
t.Errorf("URL.Path - want: %s, got: %s", functionPath, upstream.URL.Path)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
}
|
91
gateway/vendor/github.com/openfaas/faas-provider/serve.go
generated
vendored
91
gateway/vendor/github.com/openfaas/faas-provider/serve.go
generated
vendored
@ -1,91 +0,0 @@
|
||||
// Copyright (c) Alex Ellis 2017. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/openfaas/faas-provider/auth"
|
||||
"github.com/openfaas/faas-provider/types"
|
||||
)
|
||||
|
||||
var r *mux.Router
|
||||
|
||||
// Mark this as a Golang "package"
|
||||
func init() {
|
||||
r = mux.NewRouter()
|
||||
}
|
||||
|
||||
// Router gives access to the underlying router for when new routes need to be added.
|
||||
func Router() *mux.Router {
|
||||
return r
|
||||
}
|
||||
|
||||
// Serve load your handlers into the correct OpenFaaS route spec. This function is blocking.
|
||||
func Serve(handlers *types.FaaSHandlers, config *types.FaaSConfig) {
|
||||
|
||||
if config.EnableBasicAuth {
|
||||
reader := auth.ReadBasicAuthFromDisk{
|
||||
SecretMountPath: config.SecretMountPath,
|
||||
}
|
||||
|
||||
credentials, err := reader.Read()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
handlers.FunctionReader = auth.DecorateWithBasicAuth(handlers.FunctionReader, credentials)
|
||||
handlers.DeployHandler = auth.DecorateWithBasicAuth(handlers.DeployHandler, credentials)
|
||||
handlers.DeleteHandler = auth.DecorateWithBasicAuth(handlers.DeleteHandler, credentials)
|
||||
handlers.UpdateHandler = auth.DecorateWithBasicAuth(handlers.UpdateHandler, credentials)
|
||||
handlers.ReplicaReader = auth.DecorateWithBasicAuth(handlers.ReplicaReader, credentials)
|
||||
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
|
||||
r.HandleFunc("/system/functions", handlers.FunctionReader).Methods("GET")
|
||||
r.HandleFunc("/system/functions", handlers.DeployHandler).Methods("POST")
|
||||
r.HandleFunc("/system/functions", handlers.DeleteHandler).Methods("DELETE")
|
||||
r.HandleFunc("/system/functions", handlers.UpdateHandler).Methods("PUT")
|
||||
|
||||
r.HandleFunc("/system/function/{name:[-a-zA-Z_0-9]+}", handlers.ReplicaReader).Methods("GET")
|
||||
r.HandleFunc("/system/scale-function/{name:[-a-zA-Z_0-9]+}", handlers.ReplicaUpdater).Methods("POST")
|
||||
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)
|
||||
r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}/", handlers.FunctionProxy)
|
||||
r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}/{params:.*}", handlers.FunctionProxy)
|
||||
|
||||
if config.EnableHealth {
|
||||
r.HandleFunc("/healthz", handlers.HealthHandler).Methods("GET")
|
||||
}
|
||||
|
||||
readTimeout := config.ReadTimeout
|
||||
writeTimeout := config.WriteTimeout
|
||||
|
||||
tcpPort := 8080
|
||||
if config.TCPPort != nil {
|
||||
tcpPort = *config.TCPPort
|
||||
}
|
||||
|
||||
s := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", tcpPort),
|
||||
ReadTimeout: readTimeout,
|
||||
WriteTimeout: writeTimeout,
|
||||
MaxHeaderBytes: http.DefaultMaxHeaderBytes, // 1MB - can be overridden by setting Server.MaxHeaderBytes.
|
||||
Handler: r,
|
||||
}
|
||||
|
||||
log.Fatal(s.ListenAndServe())
|
||||
}
|
15
gateway/vendor/github.com/openfaas/faas-provider/types/model.go
generated
vendored
15
gateway/vendor/github.com/openfaas/faas-provider/types/model.go
generated
vendored
@ -46,6 +46,9 @@ type FunctionDeployment struct {
|
||||
// ReadOnlyRootFilesystem removes write-access from the root filesystem
|
||||
// mount-point.
|
||||
ReadOnlyRootFilesystem bool `json:"readOnlyRootFilesystem"`
|
||||
|
||||
// Namespace for the function to be deployed into
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
// FunctionResources Memory and CPU
|
||||
@ -72,7 +75,8 @@ type FunctionStatus struct {
|
||||
// EnvProcess is the process to pass to the watchdog, if in use
|
||||
EnvProcess string `json:"envProcess"`
|
||||
|
||||
// AvailableReplicas is the count of replicas ready to receive invocations as reported by the backend
|
||||
// AvailableReplicas is the count of replicas ready to receive
|
||||
// invocations as reported by the backend
|
||||
AvailableReplicas uint64 `json:"availableReplicas"`
|
||||
|
||||
// Labels are metadata for functions which may be used by the
|
||||
@ -82,4 +86,13 @@ type FunctionStatus struct {
|
||||
// Annotations are metadata for functions which may be used by the
|
||||
// backend for management, orchestration, events and build tasks
|
||||
Annotations *map[string]string `json:"annotations"`
|
||||
|
||||
// Namespace where the function can be accessed
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
// Secret for underlying orchestrator
|
||||
type Secret struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
|
1
gateway/vendor/github.com/openfaas/nats-queue-worker/.DEREK.yml
generated
vendored
1
gateway/vendor/github.com/openfaas/nats-queue-worker/.DEREK.yml
generated
vendored
@ -1 +0,0 @@
|
||||
redirect: https://raw.githubusercontent.com/openfaas/faas/master/.DEREK.yml
|
35
gateway/vendor/github.com/openfaas/nats-queue-worker/.github/ISSUE_TEMPLATE.md
generated
vendored
35
gateway/vendor/github.com/openfaas/nats-queue-worker/.github/ISSUE_TEMPLATE.md
generated
vendored
@ -1,35 +0,0 @@
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Expected Behaviour
|
||||
<!--- If you're describing a bug, tell us what should happen -->
|
||||
<!--- If you're suggesting a change/improvement, tell us how it should work -->
|
||||
|
||||
## Current Behaviour
|
||||
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
|
||||
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
|
||||
|
||||
## Possible Solution
|
||||
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
|
||||
<!--- or ideas how to implement the addition or change -->
|
||||
|
||||
## Steps to Reproduce (for bugs)
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||
<!--- reproduce this bug. Include code to reproduce, if relevant -->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
## Context
|
||||
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
||||
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
|
||||
|
||||
## Your Environment
|
||||
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
||||
* Docker version `docker version` (e.g. Docker 17.0.05 ):
|
||||
|
||||
* Are you using Docker Swarm or Kubernetes (FaaS-netes)?
|
||||
|
||||
* Operating System and version (e.g. Linux, Windows, MacOS):
|
||||
|
||||
* Link to your project or a code example to reproduce issue:
|
31
gateway/vendor/github.com/openfaas/nats-queue-worker/.github/PULL_REQUEST_TEMPLATE.md
generated
vendored
31
gateway/vendor/github.com/openfaas/nats-queue-worker/.github/PULL_REQUEST_TEMPLATE.md
generated
vendored
@ -1,31 +0,0 @@
|
||||
<!--- Provide a general summary of your changes in the Title above -->
|
||||
|
||||
## Description
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
## Motivation and Context
|
||||
<!--- Why is this change required? What problem does it solve? -->
|
||||
<!--- If it fixes an open issue, please link to the issue here. -->
|
||||
- [ ] I have raised an issue to propose this change ([required](https://github.com/openfaas/faas/blob/master/CONTRIBUTING.md))
|
||||
|
||||
## How Has This Been Tested?
|
||||
<!--- Please describe in detail how you tested your changes. -->
|
||||
<!--- Include details of your testing environment, and the tests you ran to -->
|
||||
<!--- see how your change affects other areas of the code, etc. -->
|
||||
|
||||
## Types of changes
|
||||
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||
|
||||
## Checklist:
|
||||
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||
- [ ] My code follows the code style of this project.
|
||||
- [ ] My change requires a change to the documentation.
|
||||
- [ ] I have updated the documentation accordingly.
|
||||
- [ ] I've read the [CONTRIBUTION](https://github.com/openfaas/faas/blob/master/CONTRIBUTING.md) guide
|
||||
- [ ] I have signed-off my commits with `git commit -s`
|
||||
- [ ] I have added tests to cover my changes.
|
||||
- [ ] All new and existing tests passed.
|
20
gateway/vendor/github.com/openfaas/nats-queue-worker/.gitignore
generated
vendored
20
gateway/vendor/github.com/openfaas/nats-queue-worker/.gitignore
generated
vendored
@ -1,20 +0,0 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Go
|
||||
.glide/
|
||||
.idea
|
||||
.DS_Store
|
||||
|
||||
nats-queue-worker
|
||||
queue-worker
|
||||
|
38
gateway/vendor/github.com/openfaas/nats-queue-worker/.travis.yml
generated
vendored
38
gateway/vendor/github.com/openfaas/nats-queue-worker/.travis.yml
generated
vendored
@ -1,38 +0,0 @@
|
||||
sudo: required
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.9.x
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- docker-ce
|
||||
|
||||
install:
|
||||
- echo "Please don't go get"
|
||||
|
||||
script:
|
||||
- ./build.sh
|
||||
|
||||
after_success:
|
||||
- if [ ! -z "$TRAVIS_TAG" ] ; then
|
||||
|
||||
if [ -z $DOCKER_NS ] ; then
|
||||
export DOCKER_NS=openfaas;
|
||||
fi
|
||||
|
||||
docker tag $DOCKER_NS/queue-worker:latest-dev $DOCKER_NS/queue-worker:$TRAVIS_TAG;
|
||||
echo $DOCKER_PASSWORD | docker login -u=$DOCKER_USERNAME --password-stdin;
|
||||
docker push $DOCKER_NS/queue-worker:$TRAVIS_TAG;
|
||||
|
||||
|
||||
docker tag $DOCKER_NS/queue-worker:latest-dev quay.io/$DOCKER_NS/queue-worker:$TRAVIS_TAG;
|
||||
echo $QUAY_PASSWORD | docker login -u=$QUAY_USERNAME --password-stdin quay.io;
|
||||
docker push quay.io/$DOCKER_NS/queue-worker:$TRAVIS_TAG;
|
||||
|
||||
fi
|
9
gateway/vendor/github.com/openfaas/nats-queue-worker/CONTRIBUTING.md
generated
vendored
9
gateway/vendor/github.com/openfaas/nats-queue-worker/CONTRIBUTING.md
generated
vendored
@ -1,9 +0,0 @@
|
||||
## Contributing
|
||||
|
||||
### License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
|
||||
## Guidelines
|
||||
|
||||
See guide for [FaaS](https://github.com/openfaas/faas/blob/master/CONTRIBUTING.md) here.
|
33
gateway/vendor/github.com/openfaas/nats-queue-worker/Dockerfile
generated
vendored
33
gateway/vendor/github.com/openfaas/nats-queue-worker/Dockerfile
generated
vendored
@ -1,33 +0,0 @@
|
||||
FROM golang:1.10-alpine as golang
|
||||
WORKDIR /go/src/github.com/openfaas/nats-queue-worker
|
||||
|
||||
COPY vendor vendor
|
||||
COPY handler handler
|
||||
COPY nats nats
|
||||
COPY main.go .
|
||||
COPY types.go .
|
||||
COPY readconfig.go .
|
||||
COPY readconfig_test.go .
|
||||
COPY auth.go .
|
||||
|
||||
RUN go test -v ./...
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
|
||||
|
||||
FROM alpine:3.8
|
||||
|
||||
RUN addgroup -S app \
|
||||
&& adduser -S -g app app \
|
||||
&& apk add --no-cache ca-certificates
|
||||
|
||||
WORKDIR /home/app
|
||||
|
||||
EXPOSE 8080
|
||||
ENV http_proxy ""
|
||||
ENV https_proxy ""
|
||||
|
||||
COPY --from=golang /go/src/github.com/openfaas/nats-queue-worker/app .
|
||||
|
||||
RUN chown -R app:app ./
|
||||
|
||||
USER app
|
||||
CMD ["./app"]
|
33
gateway/vendor/github.com/openfaas/nats-queue-worker/Dockerfile.arm64
generated
vendored
33
gateway/vendor/github.com/openfaas/nats-queue-worker/Dockerfile.arm64
generated
vendored
@ -1,33 +0,0 @@
|
||||
FROM golang:1.10-alpine as golang
|
||||
WORKDIR /go/src/github.com/openfaas/nats-queue-worker
|
||||
|
||||
COPY vendor vendor
|
||||
COPY handler handler
|
||||
COPY nats nats
|
||||
COPY auth.go .
|
||||
COPY types.go .
|
||||
COPY readconfig.go .
|
||||
COPY readconfig_test.go .
|
||||
COPY main.go .
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
|
||||
|
||||
FROM alpine:3.10
|
||||
|
||||
RUN addgroup -S app \
|
||||
&& adduser -S -g app app \
|
||||
&& apk add --no-cache ca-certificates
|
||||
|
||||
WORKDIR /home/app
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENV http_proxy ""
|
||||
ENV https_proxy ""
|
||||
|
||||
COPY --from=golang /go/src/github.com/openfaas/nats-queue-worker/app .
|
||||
|
||||
RUN chown -R app:app ./
|
||||
|
||||
USER app
|
||||
CMD ["./app"]
|
33
gateway/vendor/github.com/openfaas/nats-queue-worker/Dockerfile.armhf
generated
vendored
33
gateway/vendor/github.com/openfaas/nats-queue-worker/Dockerfile.armhf
generated
vendored
@ -1,33 +0,0 @@
|
||||
FROM golang:1.10-alpine as golang
|
||||
WORKDIR /go/src/github.com/openfaas/nats-queue-worker
|
||||
|
||||
COPY vendor vendor
|
||||
COPY handler handler
|
||||
COPY nats nats
|
||||
COPY main.go .
|
||||
COPY types.go .
|
||||
COPY readconfig.go .
|
||||
COPY readconfig_test.go .
|
||||
COPY auth.go .
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
|
||||
|
||||
FROM alpine:3.10
|
||||
|
||||
RUN addgroup -S app \
|
||||
&& adduser -S -g app app \
|
||||
&& apk add --no-cache ca-certificates
|
||||
|
||||
WORKDIR /home/app
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENV http_proxy ""
|
||||
ENV https_proxy ""
|
||||
|
||||
COPY --from=golang /go/src/github.com/openfaas/nats-queue-worker/app .
|
||||
|
||||
RUN chown -R app:app ./
|
||||
|
||||
USER app
|
||||
CMD ["./app"]
|
56
gateway/vendor/github.com/openfaas/nats-queue-worker/Gopkg.lock
generated
vendored
56
gateway/vendor/github.com/openfaas/nats-queue-worker/Gopkg.lock
generated
vendored
@ -1,56 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gogo/protobuf"
|
||||
packages = [
|
||||
"gogoproto",
|
||||
"proto",
|
||||
"protoc-gen-gogo/descriptor"
|
||||
]
|
||||
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/nats-io/go-nats"
|
||||
packages = [
|
||||
".",
|
||||
"encoders/builtin",
|
||||
"util"
|
||||
]
|
||||
revision = "062418ea1c2181f52dc0f954f6204370519a868b"
|
||||
version = "v1.5.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/nats-io/go-nats-streaming"
|
||||
packages = [
|
||||
".",
|
||||
"pb"
|
||||
]
|
||||
revision = "e15a53f85e4932540600a16b56f6c4f65f58176f"
|
||||
version = "v0.4.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/nats-io/nuid"
|
||||
packages = ["."]
|
||||
revision = "289cccf02c178dc782430d534e3c1f5b72af807f"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/openfaas/faas"
|
||||
packages = ["gateway/queue"]
|
||||
revision = "bfa869ec8c0c04c26c5b0ed434bc367e712dcaef"
|
||||
version = "0.10.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/openfaas/faas-provider"
|
||||
packages = ["auth"]
|
||||
revision = "9ce928bc82cbb2642e6d534f93a7904116179e6c"
|
||||
version = "0.7.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "b568e20d336fb2625ad72caa1b479a46ed1362e320083720feabccd1abaedbee"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
46
gateway/vendor/github.com/openfaas/nats-queue-worker/Gopkg.toml
generated
vendored
46
gateway/vendor/github.com/openfaas/nats-queue-worker/Gopkg.toml
generated
vendored
@ -1,46 +0,0 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/nats-io/go-nats-streaming"
|
||||
version = "0.4.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/openfaas/faas"
|
||||
version = "0.10.2"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/nats-io/go-nats"
|
||||
version = "v1.5.0"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/openfaas/faas-provider"
|
||||
version = "0.7.0"
|
29
gateway/vendor/github.com/openfaas/nats-queue-worker/Makefile
generated
vendored
29
gateway/vendor/github.com/openfaas/nats-queue-worker/Makefile
generated
vendored
@ -1,29 +0,0 @@
|
||||
TAG?=latest
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
docker build --build-arg http_proxy="${http_proxy}" --build-arg https_proxy="${https_proxy}" -t openfaas/queue-worker:$(TAG) .
|
||||
|
||||
.PHONY: push
|
||||
push:
|
||||
docker push openfaas/queue-worker:$(TAG)
|
||||
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
||||
.PHONY: ci-armhf-build
|
||||
ci-armhf-build:
|
||||
docker build --build-arg http_proxy="${http_proxy}" --build-arg https_proxy="${https_proxy}" -t openfaas/queue-worker:$(TAG)-armhf . -f Dockerfile.armhf
|
||||
|
||||
.PHONY: ci-armhf-push
|
||||
ci-armhf-push:
|
||||
docker push openfaas/queue-worker:$(TAG)-armhf
|
||||
|
||||
.PHONY: ci-arm64-build
|
||||
ci-arm64-build:
|
||||
docker build --build-arg http_proxy="${http_proxy}" --build-arg https_proxy="${https_proxy}" -t openfaas/queue-worker:$(TAG)-arm64 . -f Dockerfile.arm64
|
||||
|
||||
.PHONY: ci-arm64-push
|
||||
ci-arm64-push:
|
||||
docker push openfaas/queue-worker:$(TAG)-arm64
|
||||
|
21
gateway/vendor/github.com/openfaas/nats-queue-worker/README.md
generated
vendored
21
gateway/vendor/github.com/openfaas/nats-queue-worker/README.md
generated
vendored
@ -1,21 +0,0 @@
|
||||
## Queue worker for OpenFaaS - NATS Streaming
|
||||
|
||||
[](https://travis-ci.org/openfaas/nats-queue-worker)
|
||||
|
||||
This is a queue-worker to enable asynchronous processing of function requests.
|
||||
|
||||
> Note: A Kafka queue-worker is under-way through a PR on the main OpenFaaS repository.
|
||||
|
||||
* [Read more in the async guide](https://github.com/openfaas/faas/blob/master/guide/asynchronous.md)
|
||||
|
||||
Hub image: [openfaas/queue-worker](https://hub.docker.com/r/openfaas/queue-worker/)
|
||||
|
||||
License: MIT
|
||||
|
||||
Screenshots from keynote / video - find out more over at https://www.openfaas.com/
|
||||
|
||||
<img width="1440" alt="screen shot 2017-10-26 at 15 55 25" src="https://user-images.githubusercontent.com/6358735/32060207-049d4afa-ba66-11e7-8fc2-f4a0a84cbdaf.png">
|
||||
|
||||
<img width="1440" alt="screen shot 2017-10-26 at 15 55 19" src="https://user-images.githubusercontent.com/6358735/32060206-047eb75c-ba66-11e7-94d3-1343ea1811db.png">
|
||||
|
||||
<img width="1440" alt="screen shot 2017-10-26 at 15 55 06" src="https://user-images.githubusercontent.com/6358735/32060205-04545692-ba66-11e7-9e6d-b800a07b9bf5.png">
|
43
gateway/vendor/github.com/openfaas/nats-queue-worker/auth.go
generated
vendored
43
gateway/vendor/github.com/openfaas/nats-queue-worker/auth.go
generated
vendored
@ -1,43 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/openfaas/faas-provider/auth"
|
||||
)
|
||||
|
||||
//AddBasicAuth to a request by reading secrets
|
||||
func AddBasicAuth(req *http.Request) error {
|
||||
if os.Getenv("basic_auth") == "true" {
|
||||
reader := auth.ReadBasicAuthFromDisk{}
|
||||
|
||||
if len(os.Getenv("secret_mount_path")) > 0 {
|
||||
reader.SecretMountPath = os.Getenv("secret_mount_path")
|
||||
}
|
||||
|
||||
credentials, err := reader.Read()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read basic auth: %s", err.Error())
|
||||
}
|
||||
|
||||
req.SetBasicAuth(credentials.User, credentials.Password)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//LoadCredentials load credentials from dis
|
||||
func LoadCredentials() (*auth.BasicAuthCredentials, error) {
|
||||
reader := auth.ReadBasicAuthFromDisk{}
|
||||
|
||||
if len(os.Getenv("secret_mount_path")) > 0 {
|
||||
reader.SecretMountPath = os.Getenv("secret_mount_path")
|
||||
}
|
||||
|
||||
credentials, err := reader.Read()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read basic auth: %s", err.Error())
|
||||
}
|
||||
return credentials, nil
|
||||
}
|
11
gateway/vendor/github.com/openfaas/nats-queue-worker/build.sh
generated
vendored
11
gateway/vendor/github.com/openfaas/nats-queue-worker/build.sh
generated
vendored
@ -1,11 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
export eTAG="latest-dev"
|
||||
echo $1
|
||||
if [ $1 ] ; then
|
||||
eTAG=$1
|
||||
fi
|
||||
|
||||
echo Building openfaas/queue-worker:$eTAG
|
||||
|
||||
docker build --build-arg http_proxy=$http_proxy -t openfaas/queue-worker:$eTAG .
|
40
gateway/vendor/github.com/openfaas/nats-queue-worker/handler/handler_test.go
generated
vendored
40
gateway/vendor/github.com/openfaas/nats-queue-worker/handler/handler_test.go
generated
vendored
@ -1,40 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/openfaas/nats-queue-worker/nats"
|
||||
)
|
||||
|
||||
func Test_GetClientID_ContainsHostname(t *testing.T) {
|
||||
c := DefaultNATSConfig{}
|
||||
|
||||
val := c.GetClientID()
|
||||
|
||||
hostname, _ := os.Hostname()
|
||||
encodedHostname := nats.GetClientID(hostname)
|
||||
if !strings.HasSuffix(val, encodedHostname) {
|
||||
t.Errorf("GetClientID should contain hostname as suffix, got: %s", val)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreategetClientID(t *testing.T) {
|
||||
clientID := getClientID("computer-a")
|
||||
want := "faas-publisher-computer-a"
|
||||
if clientID != want {
|
||||
t.Logf("Want clientID: `%s`, but got: `%s`\n", want, clientID)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreategetClientIDWhenHostHasUnsupportedCharacters(t *testing.T) {
|
||||
clientID := getClientID("computer-a.acme.com")
|
||||
want := "faas-publisher-computer-a_acme_com"
|
||||
if clientID != want {
|
||||
t.Logf("Want clientID: `%s`, but got: `%s`\n", want, clientID)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
305
gateway/vendor/github.com/openfaas/nats-queue-worker/main.go
generated
vendored
305
gateway/vendor/github.com/openfaas/nats-queue-worker/main.go
generated
vendored
@ -1,305 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
stan "github.com/nats-io/go-nats-streaming"
|
||||
"github.com/openfaas/faas-provider/auth"
|
||||
"github.com/openfaas/faas/gateway/queue"
|
||||
"github.com/openfaas/nats-queue-worker/nats"
|
||||
)
|
||||
|
||||
func main() {
|
||||
readConfig := ReadConfig{}
|
||||
config := readConfig.Read()
|
||||
log.SetFlags(0)
|
||||
|
||||
hostname, _ := os.Hostname()
|
||||
|
||||
var durable string
|
||||
var unsubscribe bool
|
||||
var credentials *auth.BasicAuthCredentials
|
||||
var err error
|
||||
|
||||
if os.Getenv("basic_auth") == "true" {
|
||||
log.Printf("Loading basic authentication credentials")
|
||||
credentials, err = LoadCredentials()
|
||||
if err != nil {
|
||||
log.Printf("Error with LoadCredentials: %s ", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
client := makeClient()
|
||||
|
||||
i := 0
|
||||
messageHandler := func(msg *stan.Msg) {
|
||||
i++
|
||||
|
||||
log.Printf("[#%d] Received on [%s]: '%s'\n", i, msg.Subject, msg)
|
||||
|
||||
started := time.Now()
|
||||
|
||||
req := queue.Request{}
|
||||
unmarshalErr := json.Unmarshal(msg.Data, &req)
|
||||
|
||||
if unmarshalErr != nil {
|
||||
log.Printf("Unmarshal error: %s with data %s", unmarshalErr, msg.Data)
|
||||
return
|
||||
}
|
||||
|
||||
xCallID := req.Header.Get("X-Call-Id")
|
||||
|
||||
fmt.Printf("Request for %s.\n", req.Function)
|
||||
|
||||
if config.DebugPrintBody {
|
||||
fmt.Println(string(req.Body))
|
||||
}
|
||||
|
||||
queryString := ""
|
||||
if len(req.QueryString) > 0 {
|
||||
queryString = fmt.Sprintf("?%s", strings.TrimLeft(req.QueryString, "?"))
|
||||
}
|
||||
|
||||
functionURL := fmt.Sprintf("http://%s%s:8080/%s", req.Function, config.FunctionSuffix, queryString)
|
||||
|
||||
request, err := http.NewRequest(http.MethodPost, functionURL, bytes.NewReader(req.Body))
|
||||
defer request.Body.Close()
|
||||
|
||||
copyHeaders(request.Header, &req.Header)
|
||||
|
||||
res, err := client.Do(request)
|
||||
var status int
|
||||
var functionResult []byte
|
||||
|
||||
if err != nil {
|
||||
status = http.StatusServiceUnavailable
|
||||
|
||||
log.Println(err)
|
||||
timeTaken := time.Since(started).Seconds()
|
||||
|
||||
if req.CallbackURL != nil {
|
||||
log.Printf("Callback to: %s\n", req.CallbackURL.String())
|
||||
|
||||
resultStatusCode, resultErr := postResult(&client,
|
||||
res,
|
||||
functionResult,
|
||||
req.CallbackURL.String(),
|
||||
xCallID,
|
||||
status)
|
||||
if resultErr != nil {
|
||||
log.Println(resultErr)
|
||||
} else {
|
||||
log.Printf("Posted result: %d", resultStatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
statusCode, reportErr := postReport(&client, req.Function, status, timeTaken, config.GatewayAddress, credentials)
|
||||
if reportErr != nil {
|
||||
log.Println(reportErr)
|
||||
} else {
|
||||
log.Printf("Posting report - %d\n", statusCode)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if res.Body != nil {
|
||||
defer res.Body.Close()
|
||||
|
||||
resData, err := ioutil.ReadAll(res.Body)
|
||||
functionResult = resData
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
if config.WriteDebug {
|
||||
fmt.Println(string(functionResult))
|
||||
} else {
|
||||
fmt.Printf("Wrote %d Bytes\n", len(string(functionResult)))
|
||||
}
|
||||
}
|
||||
|
||||
timeTaken := time.Since(started).Seconds()
|
||||
|
||||
fmt.Println(res.Status)
|
||||
|
||||
if req.CallbackURL != nil {
|
||||
log.Printf("Callback to: %s\n", req.CallbackURL.String())
|
||||
resultStatusCode, resultErr := postResult(&client,
|
||||
res,
|
||||
functionResult,
|
||||
req.CallbackURL.String(),
|
||||
xCallID,
|
||||
res.StatusCode)
|
||||
if resultErr != nil {
|
||||
log.Println(resultErr)
|
||||
} else {
|
||||
log.Printf("Posted result: %d", resultStatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
statusCode, reportErr := postReport(&client, req.Function, res.StatusCode, timeTaken, config.GatewayAddress, credentials)
|
||||
|
||||
if reportErr != nil {
|
||||
log.Println(reportErr)
|
||||
} else {
|
||||
log.Printf("Posting report - %d\n", statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
natsURL := "nats://" + config.NatsAddress + ":4222"
|
||||
|
||||
natsQueue := NATSQueue{
|
||||
clusterID: "faas-cluster",
|
||||
clientID: "faas-worker-" + nats.GetClientID(hostname),
|
||||
natsURL: natsURL,
|
||||
|
||||
connMutex: &sync.RWMutex{},
|
||||
maxReconnect: config.MaxReconnect,
|
||||
reconnectDelay: config.ReconnectDelay,
|
||||
quitCh: make(chan struct{}),
|
||||
|
||||
subject: "faas-request",
|
||||
qgroup: "faas",
|
||||
durable: durable,
|
||||
messageHandler: messageHandler,
|
||||
startOption: stan.StartWithLastReceived(),
|
||||
maxInFlight: stan.MaxInflight(config.MaxInflight),
|
||||
ackWait: config.AckWait,
|
||||
}
|
||||
|
||||
if initErr := natsQueue.connect(); initErr != nil {
|
||||
log.Panic(initErr)
|
||||
}
|
||||
|
||||
// Wait for a SIGINT (perhaps triggered by user with CTRL-C)
|
||||
// Run cleanup when signal is received
|
||||
signalChan := make(chan os.Signal, 1)
|
||||
cleanupDone := make(chan bool)
|
||||
signal.Notify(signalChan, os.Interrupt)
|
||||
go func() {
|
||||
for range signalChan {
|
||||
fmt.Printf("\nReceived an interrupt, unsubscribing and closing connection...\n\n")
|
||||
// Do not unsubscribe a durable on exit, except if asked to.
|
||||
if durable == "" || unsubscribe {
|
||||
if err := natsQueue.unsubscribe(); err != nil {
|
||||
log.Panicf(
|
||||
"Cannot unsubscribe subject: %s from %s because of an error: %v",
|
||||
natsQueue.subject,
|
||||
natsQueue.natsURL,
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
if err := natsQueue.closeConnection(); err != nil {
|
||||
log.Panicf("Cannot close connection to %s because of an error: %v\n", natsQueue.natsURL, err)
|
||||
}
|
||||
cleanupDone <- true
|
||||
}
|
||||
}()
|
||||
<-cleanupDone
|
||||
}
|
||||
|
||||
// makeClient constructs a HTTP client with keep-alive turned
|
||||
// off and a dial-timeout of 30 seconds.
|
||||
func makeClient() http.Client {
|
||||
proxyClient := http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 0,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 1,
|
||||
DisableKeepAlives: true,
|
||||
IdleConnTimeout: 120 * time.Millisecond,
|
||||
ExpectContinueTimeout: 1500 * time.Millisecond,
|
||||
},
|
||||
}
|
||||
return proxyClient
|
||||
}
|
||||
|
||||
func postResult(client *http.Client, functionRes *http.Response, result []byte, callbackURL string, xCallID string, statusCode int) (int, error) {
|
||||
var reader io.Reader
|
||||
|
||||
if result != nil {
|
||||
reader = bytes.NewReader(result)
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(http.MethodPost, callbackURL, reader)
|
||||
|
||||
if functionRes != nil {
|
||||
copyHeaders(request.Header, &functionRes.Header)
|
||||
}
|
||||
|
||||
request.Header.Set("X-Function-Status", fmt.Sprintf("%d", statusCode))
|
||||
|
||||
if len(xCallID) > 0 {
|
||||
request.Header.Set("X-Call-Id", xCallID)
|
||||
}
|
||||
|
||||
res, err := client.Do(request)
|
||||
|
||||
if err != nil {
|
||||
return http.StatusBadGateway, fmt.Errorf("error posting result to URL %s %s", callbackURL, err.Error())
|
||||
}
|
||||
|
||||
if request.Body != nil {
|
||||
defer request.Body.Close()
|
||||
}
|
||||
|
||||
if res.Body != nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
return res.StatusCode, nil
|
||||
}
|
||||
|
||||
func copyHeaders(destination http.Header, source *http.Header) {
|
||||
for k, v := range *source {
|
||||
vClone := make([]string, len(v))
|
||||
copy(vClone, v)
|
||||
(destination)[k] = vClone
|
||||
}
|
||||
}
|
||||
|
||||
func postReport(client *http.Client, function string, statusCode int, timeTaken float64, gatewayAddress string, credentials *auth.BasicAuthCredentials) (int, error) {
|
||||
req := AsyncReport{
|
||||
FunctionName: function,
|
||||
StatusCode: statusCode,
|
||||
TimeTaken: timeTaken,
|
||||
}
|
||||
|
||||
targetPostback := "http://" + gatewayAddress + ":8080/system/async-report"
|
||||
reqBytes, _ := json.Marshal(req)
|
||||
request, err := http.NewRequest(http.MethodPost, targetPostback, bytes.NewReader(reqBytes))
|
||||
|
||||
if os.Getenv("basic_auth") == "true" && credentials != nil {
|
||||
request.SetBasicAuth(credentials.User, credentials.Password)
|
||||
}
|
||||
|
||||
defer request.Body.Close()
|
||||
|
||||
res, err := client.Do(request)
|
||||
|
||||
if err != nil {
|
||||
return http.StatusGatewayTimeout, fmt.Errorf("cannot post report to %s: %s", targetPostback, err)
|
||||
}
|
||||
|
||||
if res.Body != nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
return res.StatusCode, nil
|
||||
}
|
23
gateway/vendor/github.com/openfaas/nats-queue-worker/nats/client_test.go
generated
vendored
23
gateway/vendor/github.com/openfaas/nats-queue-worker/nats/client_test.go
generated
vendored
@ -1,23 +0,0 @@
|
||||
package nats
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetClientID(t *testing.T) {
|
||||
clientID := GetClientID("computer-a")
|
||||
want := "computer-a"
|
||||
if clientID != want {
|
||||
t.Logf("Want clientID: `%s`, but got: `%s`\n", want, clientID)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetClientIDWhenHostHasUnsupportedCharacters(t *testing.T) {
|
||||
clientID := GetClientID("computer-a.acme.com")
|
||||
want := "computer-a_acme_com"
|
||||
if clientID != want {
|
||||
t.Logf("Want clientID: `%s`, but got: `%s`\n", want, clientID)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
112
gateway/vendor/github.com/openfaas/nats-queue-worker/readconfig.go
generated
vendored
112
gateway/vendor/github.com/openfaas/nats-queue-worker/readconfig.go
generated
vendored
@ -1,112 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ReadConfig constitutes config from env variables
|
||||
type ReadConfig struct {
|
||||
}
|
||||
|
||||
const DefaultMaxReconnect = 120
|
||||
|
||||
const DefaultReconnectDelay = time.Second * 2
|
||||
|
||||
func (ReadConfig) Read() QueueWorkerConfig {
|
||||
cfg := QueueWorkerConfig{
|
||||
AckWait: time.Second * 30,
|
||||
MaxInflight: 1,
|
||||
}
|
||||
|
||||
if val, exists := os.LookupEnv("faas_nats_address"); exists {
|
||||
cfg.NatsAddress = val
|
||||
} else {
|
||||
cfg.NatsAddress = "nats"
|
||||
}
|
||||
|
||||
if val, exists := os.LookupEnv("faas_gateway_address"); exists {
|
||||
cfg.GatewayAddress = val
|
||||
} else {
|
||||
cfg.GatewayAddress = "gateway"
|
||||
}
|
||||
|
||||
if val, exists := os.LookupEnv("faas_function_suffix"); exists {
|
||||
cfg.FunctionSuffix = val
|
||||
}
|
||||
|
||||
if val, exists := os.LookupEnv("faas_print_body"); exists {
|
||||
if val == "1" || val == "true" {
|
||||
cfg.DebugPrintBody = true
|
||||
} else {
|
||||
cfg.DebugPrintBody = false
|
||||
}
|
||||
}
|
||||
|
||||
if val, exists := os.LookupEnv("write_debug"); exists {
|
||||
if val == "1" || val == "true" {
|
||||
cfg.WriteDebug = true
|
||||
} else {
|
||||
cfg.WriteDebug = false
|
||||
}
|
||||
}
|
||||
|
||||
if value, exists := os.LookupEnv("max_inflight"); exists {
|
||||
val, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
log.Println("max_inflight error:", err)
|
||||
} else {
|
||||
cfg.MaxInflight = val
|
||||
}
|
||||
}
|
||||
|
||||
cfg.MaxReconnect = DefaultMaxReconnect
|
||||
|
||||
if value, exists := os.LookupEnv("faas_max_reconnect"); exists {
|
||||
val, err := strconv.Atoi(value)
|
||||
|
||||
if err != nil {
|
||||
log.Println("converting faas_max_reconnect to int error:", err)
|
||||
} else {
|
||||
cfg.MaxReconnect = val
|
||||
}
|
||||
}
|
||||
|
||||
cfg.ReconnectDelay = DefaultReconnectDelay
|
||||
|
||||
if value, exists := os.LookupEnv("faas_reconnect_delay"); exists {
|
||||
reconnectDelayVal, durationErr := time.ParseDuration(value)
|
||||
|
||||
if durationErr != nil {
|
||||
log.Println("parse env var: faas_reconnect_delay as time.Duration error:", durationErr)
|
||||
|
||||
} else {
|
||||
cfg.ReconnectDelay = reconnectDelayVal
|
||||
}
|
||||
}
|
||||
|
||||
if val, exists := os.LookupEnv("ack_wait"); exists {
|
||||
ackWaitVal, durationErr := time.ParseDuration(val)
|
||||
if durationErr != nil {
|
||||
log.Println("ack_wait error:", durationErr)
|
||||
} else {
|
||||
cfg.AckWait = ackWaitVal
|
||||
}
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
type QueueWorkerConfig struct {
|
||||
NatsAddress string
|
||||
GatewayAddress string
|
||||
FunctionSuffix string
|
||||
DebugPrintBody bool
|
||||
WriteDebug bool
|
||||
MaxInflight int
|
||||
AckWait time.Duration
|
||||
MaxReconnect int
|
||||
ReconnectDelay time.Duration
|
||||
}
|
96
gateway/vendor/github.com/openfaas/nats-queue-worker/readconfig_test.go
generated
vendored
96
gateway/vendor/github.com/openfaas/nats-queue-worker/readconfig_test.go
generated
vendored
@ -1,96 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_ReadConfig(t *testing.T) {
|
||||
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
os.Setenv("faas_nats_address", "test_nats")
|
||||
os.Setenv("faas_gateway_address", "test_gatewayaddr")
|
||||
os.Setenv("faas_function_suffix", "test_suffix")
|
||||
os.Setenv("faas_print_body", "true")
|
||||
os.Setenv("write_debug", "true")
|
||||
os.Setenv("max_inflight", "10")
|
||||
os.Setenv("ack_wait", "10ms")
|
||||
|
||||
config := readConfig.Read()
|
||||
|
||||
expected := "test_nats"
|
||||
if config.NatsAddress != expected {
|
||||
t.Logf("Expected NatsAddress `%s` actual `%s`\n", expected, config.NatsAddress)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
expected = "test_gatewayaddr"
|
||||
if config.GatewayAddress != expected {
|
||||
t.Logf("Expected GatewayAddress `%s` actual `%s`\n", expected, config.GatewayAddress)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
expected = "test_suffix"
|
||||
if config.FunctionSuffix != expected {
|
||||
t.Logf("Expected FunctionSuffix `%s` actual `%s`\n", expected, config.FunctionSuffix)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if config.DebugPrintBody != true {
|
||||
t.Logf("Expected DebugPrintBody `%v` actual `%v`\n", true, config.DebugPrintBody)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if config.WriteDebug != true {
|
||||
t.Logf("Expected WriteDebug `%v` actual `%v`\n", true, config.WriteDebug)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
expectedMaxInflight := 10
|
||||
if config.MaxInflight != expectedMaxInflight {
|
||||
t.Logf("Expected maxInflight `%v` actual `%v`\n", expectedMaxInflight, config.MaxInflight)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
expectedAckWait := time.Millisecond * 10
|
||||
if config.AckWait != expectedAckWait {
|
||||
t.Logf("Expected maxInflight `%v` actual `%v`\n", expectedAckWait, config.AckWait)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
os.Unsetenv("max_inflight")
|
||||
os.Unsetenv("ack_wait")
|
||||
|
||||
config = readConfig.Read()
|
||||
|
||||
expectedMaxInflight = 1
|
||||
if config.MaxInflight != expectedMaxInflight {
|
||||
t.Logf("Expected maxInflight `%v` actual `%v`\n", expectedMaxInflight, config.MaxInflight)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
expectedAckWait = time.Second * 30
|
||||
if config.AckWait != expectedAckWait {
|
||||
t.Logf("Expected maxInflight `%v` actual `%v`\n", expectedAckWait, config.AckWait)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
os.Setenv("max_inflight", "10.00")
|
||||
os.Setenv("ack_wait", "10")
|
||||
|
||||
config = readConfig.Read()
|
||||
|
||||
expectedMaxInflight = 1
|
||||
if config.MaxInflight != expectedMaxInflight {
|
||||
t.Logf("Expected maxInflight `%v` actual `%v`\n", expectedMaxInflight, config.MaxInflight)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
expectedAckWait = time.Second * 30
|
||||
if config.AckWait != expectedAckWait {
|
||||
t.Logf("Expected ackWait `%v` actual `%v`\n", expectedAckWait, config.AckWait)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
142
gateway/vendor/github.com/openfaas/nats-queue-worker/types.go
generated
vendored
142
gateway/vendor/github.com/openfaas/nats-queue-worker/types.go
generated
vendored
@ -1,142 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
stan "github.com/nats-io/go-nats-streaming"
|
||||
)
|
||||
|
||||
// AsyncReport is the report from a function executed on a queue worker.
|
||||
type AsyncReport struct {
|
||||
FunctionName string `json:"name"`
|
||||
StatusCode int `json:"statusCode"`
|
||||
TimeTaken float64 `json:"timeTaken"`
|
||||
}
|
||||
|
||||
// NATSQueue represents a subscription to NATS Streaming
|
||||
type NATSQueue struct {
|
||||
clusterID string
|
||||
clientID string
|
||||
natsURL string
|
||||
|
||||
maxReconnect int
|
||||
reconnectDelay time.Duration
|
||||
conn stan.Conn
|
||||
connMutex *sync.RWMutex
|
||||
quitCh chan struct{}
|
||||
|
||||
subject string
|
||||
qgroup string
|
||||
durable string
|
||||
ackWait time.Duration
|
||||
messageHandler func(*stan.Msg)
|
||||
startOption stan.SubscriptionOption
|
||||
maxInFlight stan.SubscriptionOption
|
||||
subscription stan.Subscription
|
||||
}
|
||||
|
||||
// connect creates a subscription to NATS Streaming
|
||||
func (q *NATSQueue) connect() error {
|
||||
log.Printf("Connect: %s\n", q.natsURL)
|
||||
|
||||
nc, err := stan.Connect(
|
||||
q.clusterID,
|
||||
q.clientID,
|
||||
stan.NatsURL(q.natsURL),
|
||||
stan.SetConnectionLostHandler(func(conn stan.Conn, err error) {
|
||||
log.Printf("Disconnected from %s\n", q.natsURL)
|
||||
|
||||
q.reconnect()
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't connect to %s: %v", q.natsURL, err)
|
||||
}
|
||||
|
||||
q.connMutex.Lock()
|
||||
defer q.connMutex.Unlock()
|
||||
|
||||
q.conn = nc
|
||||
|
||||
log.Printf("Subscribing to: %s at %s\n", q.subject, q.natsURL)
|
||||
log.Println("Wait for ", q.ackWait)
|
||||
|
||||
subscription, err := q.conn.QueueSubscribe(
|
||||
q.subject,
|
||||
q.qgroup,
|
||||
q.messageHandler,
|
||||
stan.DurableName(q.durable),
|
||||
stan.AckWait(q.ackWait),
|
||||
q.startOption,
|
||||
q.maxInFlight,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't subscribe to %s at %s. Error: %v", q.subject, q.natsURL, err)
|
||||
}
|
||||
|
||||
log.Printf(
|
||||
"Listening on [%s], clientID=[%s], qgroup=[%s] durable=[%s]\n",
|
||||
q.subject,
|
||||
q.clientID,
|
||||
q.qgroup,
|
||||
q.durable,
|
||||
)
|
||||
|
||||
q.subscription = subscription
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *NATSQueue) reconnect() {
|
||||
log.Printf("Reconnect\n")
|
||||
|
||||
for i := 0; i < q.maxReconnect; i++ {
|
||||
select {
|
||||
case <-time.After(time.Duration(i) * q.reconnectDelay):
|
||||
if err := q.connect(); err == nil {
|
||||
log.Printf("Reconnecting (%d/%d) to %s succeeded\n", i+1, q.maxReconnect, q.natsURL)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
nextTryIn := (time.Duration(i+1) * q.reconnectDelay).String()
|
||||
|
||||
log.Printf("Reconnecting (%d/%d) to %s failed\n", i+1, q.maxReconnect, q.natsURL)
|
||||
log.Printf("Waiting %s before next try", nextTryIn)
|
||||
case <-q.quitCh:
|
||||
log.Println("Received signal to stop reconnecting...")
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Reconnecting limit (%d) reached\n", q.maxReconnect)
|
||||
}
|
||||
|
||||
func (q *NATSQueue) unsubscribe() error {
|
||||
q.connMutex.Lock()
|
||||
defer q.connMutex.Unlock()
|
||||
|
||||
if q.subscription != nil {
|
||||
return fmt.Errorf("q.subscription is nil")
|
||||
}
|
||||
|
||||
return q.subscription.Unsubscribe()
|
||||
}
|
||||
|
||||
func (q *NATSQueue) closeConnection() error {
|
||||
q.connMutex.Lock()
|
||||
defer q.connMutex.Unlock()
|
||||
|
||||
if q.conn == nil {
|
||||
return fmt.Errorf("q.conn is nil")
|
||||
}
|
||||
|
||||
close(q.quitCh)
|
||||
|
||||
return q.conn.Close()
|
||||
}
|
Reference in New Issue
Block a user