mirror of
https://github.com/openfaas/faas.git
synced 2025-06-23 23:33:25 +00:00
Support external URL for FaaS functions
This commit is contained in:
@ -7,7 +7,7 @@ services:
|
|||||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
image: functions/gateway:0.5.6
|
image: functions/gateway:latest-dev
|
||||||
networks:
|
networks:
|
||||||
- functions
|
- functions
|
||||||
environment:
|
environment:
|
||||||
|
@ -1,13 +1,5 @@
|
|||||||
FROM golang:1.7.5
|
FROM golang:1.7.5
|
||||||
|
|
||||||
#RUN go get -d github.com/docker/docker/api/types \
|
|
||||||
# && go get -d github.com/docker/docker/api/types/filters \
|
|
||||||
# && go get -d github.com/docker/docker/api/types/swarm \
|
|
||||||
# && go get -d github.com/docker/docker/client \
|
|
||||||
# && go get github.com/gorilla/mux \
|
|
||||||
# && go get github.com/prometheus/client_golang/prometheus
|
|
||||||
#RUN go get -d github.com/Sirupsen/logrus
|
|
||||||
|
|
||||||
WORKDIR /go/src/github.com/alexellis/faas/gateway
|
WORKDIR /go/src/github.com/alexellis/faas/gateway
|
||||||
|
|
||||||
COPY vendor vendor
|
COPY vendor vendor
|
||||||
@ -17,7 +9,7 @@ COPY metrics metrics
|
|||||||
COPY requests requests
|
COPY requests requests
|
||||||
COPY tests tests
|
COPY tests tests
|
||||||
COPY server.go .
|
COPY server.go .
|
||||||
COPY readconfig.go .
|
COPY types types
|
||||||
|
|
||||||
RUN go test -v ./tests && \
|
RUN go test -v ./tests && \
|
||||||
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
|
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
|
||||||
|
@ -16,7 +16,7 @@ COPY tests tests
|
|||||||
COPY handlers handlers
|
COPY handlers handlers
|
||||||
|
|
||||||
COPY server.go .
|
COPY server.go .
|
||||||
COPY readconfig.go .
|
COPY types types
|
||||||
|
|
||||||
RUN go test -v ./tests && \
|
RUN go test -v ./tests && \
|
||||||
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
|
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
|
||||||
|
@ -8,7 +8,7 @@ COPY metrics metrics
|
|||||||
COPY requests requests
|
COPY requests requests
|
||||||
COPY tests tests
|
COPY tests tests
|
||||||
COPY server.go .
|
COPY server.go .
|
||||||
COPY readconfig.go .
|
COPY types types
|
||||||
|
|
||||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gateway .
|
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gateway .
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ COPY metrics metrics
|
|||||||
COPY requests requests
|
COPY requests requests
|
||||||
COPY tests tests
|
COPY tests tests
|
||||||
COPY server.go .
|
COPY server.go .
|
||||||
COPY readconfig.go .
|
COPY types types
|
||||||
|
|
||||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gateway .
|
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gateway .
|
||||||
|
|
||||||
|
1
gateway/handlers/externalprovider.go
Normal file
1
gateway/handlers/externalprovider.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package handlers
|
@ -4,29 +4,44 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
faasHandlers "github.com/alexellis/faas/gateway/handlers"
|
|
||||||
"github.com/alexellis/faas/gateway/metrics"
|
|
||||||
"github.com/docker/docker/client"
|
|
||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
internalHandlers "github.com/alexellis/faas/gateway/handlers"
|
||||||
|
"github.com/alexellis/faas/gateway/metrics"
|
||||||
|
"github.com/alexellis/faas/gateway/types"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type handlerSet struct {
|
||||||
|
Proxy http.HandlerFunc
|
||||||
|
DeployFunction http.HandlerFunc
|
||||||
|
DeleteFunction http.HandlerFunc
|
||||||
|
ListFunctions http.HandlerFunc
|
||||||
|
Alert http.HandlerFunc
|
||||||
|
RoutelessProxy http.HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logger := logrus.Logger{}
|
logger := logrus.Logger{}
|
||||||
logrus.SetFormatter(&logrus.TextFormatter{})
|
logrus.SetFormatter(&logrus.TextFormatter{})
|
||||||
|
|
||||||
osEnv := OsEnv{}
|
osEnv := types.OsEnv{}
|
||||||
readConfig := ReadConfig{}
|
readConfig := types.ReadConfig{}
|
||||||
config := readConfig.Read(osEnv)
|
config := readConfig.Read(osEnv)
|
||||||
|
|
||||||
log.Printf("HTTP Read Timeout: %s", config.ReadTimeout)
|
log.Printf("HTTP Read Timeout: %s", config.ReadTimeout)
|
||||||
log.Printf("HTTP Write Timeout: %s", config.WriteTimeout)
|
log.Printf("HTTP Write Timeout: %s", config.WriteTimeout)
|
||||||
|
|
||||||
var dockerClient *client.Client
|
var dockerClient *client.Client
|
||||||
|
|
||||||
|
if config.UseExternalProvider() {
|
||||||
|
log.Printf("Binding to external function provider: %s", config.FunctionsProviderURL)
|
||||||
|
} else {
|
||||||
var err error
|
var err error
|
||||||
dockerClient, err = client.NewEnvClient()
|
dockerClient, err = client.NewEnvClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -37,34 +52,55 @@ func main() {
|
|||||||
log.Fatal("Error with Docker server.\n", err)
|
log.Fatal("Error with Docker server.\n", err)
|
||||||
}
|
}
|
||||||
log.Printf("Docker API version: %s, %s\n", dockerVersion.APIVersion, dockerVersion.Version)
|
log.Printf("Docker API version: %s, %s\n", dockerVersion.APIVersion, dockerVersion.Version)
|
||||||
|
}
|
||||||
|
|
||||||
metricsOptions := metrics.BuildMetricsOptions()
|
metricsOptions := metrics.BuildMetricsOptions()
|
||||||
metrics.RegisterMetrics(metricsOptions)
|
metrics.RegisterMetrics(metricsOptions)
|
||||||
|
|
||||||
r := mux.NewRouter()
|
var faasHandlers handlerSet
|
||||||
// r.StrictSlash(false) // This didn't work, so register routes twice.
|
|
||||||
|
|
||||||
functionHandler := faasHandlers.MakeProxy(metricsOptions, true, dockerClient, &logger)
|
if config.UseExternalProvider() {
|
||||||
r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}", functionHandler)
|
|
||||||
r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}/", functionHandler)
|
|
||||||
|
|
||||||
r.HandleFunc("/system/alert", faasHandlers.MakeAlertHandler(dockerClient))
|
reverseProxy := httputil.NewSingleHostReverseProxy(config.FunctionsProviderURL)
|
||||||
r.HandleFunc("/system/functions", faasHandlers.MakeFunctionReader(metricsOptions, dockerClient)).Methods("GET")
|
|
||||||
r.HandleFunc("/system/functions", faasHandlers.MakeNewFunctionHandler(metricsOptions, dockerClient)).Methods("POST")
|
|
||||||
r.HandleFunc("/system/functions", faasHandlers.MakeDeleteFunctionHandler(metricsOptions, dockerClient)).Methods("DELETE")
|
|
||||||
|
|
||||||
fs := http.FileServer(http.Dir("./assets/"))
|
faasHandlers.Proxy = handler(reverseProxy)
|
||||||
r.PathPrefix("/ui/").Handler(http.StripPrefix("/ui", fs)).Methods("GET")
|
faasHandlers.RoutelessProxy = handler(reverseProxy)
|
||||||
|
faasHandlers.Alert = handler(reverseProxy)
|
||||||
|
faasHandlers.ListFunctions = handler(reverseProxy)
|
||||||
|
faasHandlers.DeployFunction = handler(reverseProxy)
|
||||||
|
faasHandlers.DeleteFunction = handler(reverseProxy)
|
||||||
|
|
||||||
r.HandleFunc("/", faasHandlers.MakeProxy(metricsOptions, false, dockerClient, &logger)).Methods("POST")
|
} else {
|
||||||
|
faasHandlers.Proxy = internalHandlers.MakeProxy(metricsOptions, true, dockerClient, &logger)
|
||||||
metricsHandler := metrics.PrometheusHandler()
|
faasHandlers.RoutelessProxy = internalHandlers.MakeProxy(metricsOptions, true, dockerClient, &logger)
|
||||||
r.Handle("/metrics", metricsHandler)
|
faasHandlers.Alert = internalHandlers.MakeAlertHandler(dockerClient)
|
||||||
|
faasHandlers.ListFunctions = internalHandlers.MakeFunctionReader(metricsOptions, dockerClient)
|
||||||
|
faasHandlers.DeployFunction = internalHandlers.MakeNewFunctionHandler(metricsOptions, dockerClient)
|
||||||
|
faasHandlers.DeleteFunction = internalHandlers.MakeDeleteFunctionHandler(metricsOptions, dockerClient)
|
||||||
|
|
||||||
// This could exist in a separate process - records the replicas of each swarm service.
|
// This could exist in a separate process - records the replicas of each swarm service.
|
||||||
functionLabel := "function"
|
functionLabel := "function"
|
||||||
metrics.AttachSwarmWatcher(dockerClient, metricsOptions, functionLabel)
|
metrics.AttachSwarmWatcher(dockerClient, metricsOptions, functionLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
|
||||||
|
// r.StrictSlash(false) // This didn't work, so register routes twice.
|
||||||
|
r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}", faasHandlers.Proxy)
|
||||||
|
r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}/", faasHandlers.Proxy)
|
||||||
|
|
||||||
|
r.HandleFunc("/system/alert", faasHandlers.Alert)
|
||||||
|
r.HandleFunc("/system/functions", faasHandlers.ListFunctions).Methods("GET")
|
||||||
|
r.HandleFunc("/system/functions", faasHandlers.DeployFunction).Methods("POST")
|
||||||
|
r.HandleFunc("/system/functions", faasHandlers.DeleteFunction).Methods("DELETE")
|
||||||
|
|
||||||
|
fs := http.FileServer(http.Dir("./assets/"))
|
||||||
|
r.PathPrefix("/ui/").Handler(http.StripPrefix("/ui", fs)).Methods("GET")
|
||||||
|
|
||||||
|
r.HandleFunc("/", faasHandlers.RoutelessProxy).Methods("POST")
|
||||||
|
|
||||||
|
metricsHandler := metrics.PrometheusHandler()
|
||||||
|
r.Handle("/metrics", metricsHandler)
|
||||||
r.Handle("/", http.RedirectHandler("/ui/", http.StatusMovedPermanently)).Methods("GET")
|
r.Handle("/", http.RedirectHandler("/ui/", http.StatusMovedPermanently)).Methods("GET")
|
||||||
|
|
||||||
tcpPort := 8080
|
tcpPort := 8080
|
||||||
@ -79,3 +115,10 @@ func main() {
|
|||||||
|
|
||||||
log.Fatal(s.ListenAndServe())
|
log.Fatal(s.ListenAndServe())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handler(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Printf("Forwarding [%s] to %s", r.Method, r.URL.String())
|
||||||
|
p.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
gateway "github.com/alexellis/faas/gateway"
|
"github.com/alexellis/faas/gateway/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EnvBucket struct {
|
type EnvBucket struct {
|
||||||
@ -28,9 +28,21 @@ func (e EnvBucket) Setenv(key string, value string) {
|
|||||||
e.Items[key] = value
|
e.Items[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRead_UseExternalProvider_Defaults(t *testing.T) {
|
||||||
|
defaults := NewEnvBucket()
|
||||||
|
readConfig := types.ReadConfig{}
|
||||||
|
|
||||||
|
config := readConfig.Read(defaults)
|
||||||
|
|
||||||
|
if config.UseExternalProvider() != false {
|
||||||
|
t.Log("Default for UseExternalProvider should be false")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRead_EmptyTimeoutConfig(t *testing.T) {
|
func TestRead_EmptyTimeoutConfig(t *testing.T) {
|
||||||
defaults := NewEnvBucket()
|
defaults := NewEnvBucket()
|
||||||
readConfig := gateway.ReadConfig{}
|
readConfig := types.ReadConfig{}
|
||||||
|
|
||||||
config := readConfig.Read(defaults)
|
config := readConfig.Read(defaults)
|
||||||
|
|
||||||
@ -49,7 +61,7 @@ func TestRead_ReadAndWriteTimeoutConfig(t *testing.T) {
|
|||||||
defaults.Setenv("read_timeout", "10")
|
defaults.Setenv("read_timeout", "10")
|
||||||
defaults.Setenv("write_timeout", "60")
|
defaults.Setenv("write_timeout", "60")
|
||||||
|
|
||||||
readConfig := gateway.ReadConfig{}
|
readConfig := types.ReadConfig{}
|
||||||
config := readConfig.Read(defaults)
|
config := readConfig.Read(defaults)
|
||||||
|
|
||||||
if (config.ReadTimeout) != time.Duration(10)*time.Second {
|
if (config.ReadTimeout) != time.Duration(10)*time.Second {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package main
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@ -51,6 +53,14 @@ func (ReadConfig) Read(hasEnv HasEnv) GatewayConfig {
|
|||||||
cfg.ReadTimeout = time.Duration(readTimeout) * time.Second
|
cfg.ReadTimeout = time.Duration(readTimeout) * time.Second
|
||||||
cfg.WriteTimeout = time.Duration(writeTimeout) * time.Second
|
cfg.WriteTimeout = time.Duration(writeTimeout) * time.Second
|
||||||
|
|
||||||
|
if len(hasEnv.Getenv("functions_provider_url")) > 0 {
|
||||||
|
var err error
|
||||||
|
cfg.FunctionsProviderURL, err = url.Parse(hasEnv.Getenv("functions_provider_url"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("If functions_provider_url is provided, then it should be a valid URL.", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,4 +68,10 @@ func (ReadConfig) Read(hasEnv HasEnv) GatewayConfig {
|
|||||||
type GatewayConfig struct {
|
type GatewayConfig struct {
|
||||||
ReadTimeout time.Duration
|
ReadTimeout time.Duration
|
||||||
WriteTimeout time.Duration
|
WriteTimeout time.Duration
|
||||||
|
FunctionsProviderURL *url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseExternalProvider decide whether to bypass built-in Docker Swarm engine
|
||||||
|
func (g *GatewayConfig) UseExternalProvider() bool {
|
||||||
|
return g.FunctionsProviderURL != nil
|
||||||
}
|
}
|
Reference in New Issue
Block a user