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"
|
||||
ports:
|
||||
- 8080:8080
|
||||
image: functions/gateway:0.5.6
|
||||
image: functions/gateway:latest-dev
|
||||
networks:
|
||||
- functions
|
||||
environment:
|
||||
|
@ -1,13 +1,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
|
||||
|
||||
COPY vendor vendor
|
||||
@ -17,7 +9,7 @@ COPY metrics metrics
|
||||
COPY requests requests
|
||||
COPY tests tests
|
||||
COPY server.go .
|
||||
COPY readconfig.go .
|
||||
COPY types types
|
||||
|
||||
RUN go test -v ./tests && \
|
||||
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
|
||||
|
@ -16,7 +16,7 @@ COPY tests tests
|
||||
COPY handlers handlers
|
||||
|
||||
COPY server.go .
|
||||
COPY readconfig.go .
|
||||
COPY types types
|
||||
|
||||
RUN go test -v ./tests && \
|
||||
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
|
||||
|
@ -8,7 +8,7 @@ COPY metrics metrics
|
||||
COPY requests requests
|
||||
COPY tests tests
|
||||
COPY server.go .
|
||||
COPY readconfig.go .
|
||||
COPY types types
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gateway .
|
||||
|
||||
|
@ -9,7 +9,7 @@ COPY metrics metrics
|
||||
COPY requests requests
|
||||
COPY tests tests
|
||||
COPY server.go .
|
||||
COPY readconfig.go .
|
||||
COPY types types
|
||||
|
||||
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,67 +4,103 @@ import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
faasHandlers "github.com/alexellis/faas/gateway/handlers"
|
||||
"github.com/alexellis/faas/gateway/metrics"
|
||||
"github.com/docker/docker/client"
|
||||
"net/http/httputil"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type handlerSet struct {
|
||||
Proxy http.HandlerFunc
|
||||
DeployFunction http.HandlerFunc
|
||||
DeleteFunction http.HandlerFunc
|
||||
ListFunctions http.HandlerFunc
|
||||
Alert http.HandlerFunc
|
||||
RoutelessProxy http.HandlerFunc
|
||||
}
|
||||
|
||||
func main() {
|
||||
logger := logrus.Logger{}
|
||||
logrus.SetFormatter(&logrus.TextFormatter{})
|
||||
|
||||
osEnv := OsEnv{}
|
||||
readConfig := ReadConfig{}
|
||||
osEnv := types.OsEnv{}
|
||||
readConfig := types.ReadConfig{}
|
||||
config := readConfig.Read(osEnv)
|
||||
|
||||
log.Printf("HTTP Read Timeout: %s", config.ReadTimeout)
|
||||
log.Printf("HTTP Write Timeout: %s", config.WriteTimeout)
|
||||
|
||||
var dockerClient *client.Client
|
||||
var err error
|
||||
dockerClient, err = client.NewEnvClient()
|
||||
if err != nil {
|
||||
log.Fatal("Error with Docker client.")
|
||||
|
||||
if config.UseExternalProvider() {
|
||||
log.Printf("Binding to external function provider: %s", config.FunctionsProviderURL)
|
||||
} else {
|
||||
var err error
|
||||
dockerClient, err = client.NewEnvClient()
|
||||
if err != nil {
|
||||
log.Fatal("Error with Docker client.")
|
||||
}
|
||||
dockerVersion, err := dockerClient.ServerVersion(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal("Error with Docker server.\n", err)
|
||||
}
|
||||
log.Printf("Docker API version: %s, %s\n", dockerVersion.APIVersion, dockerVersion.Version)
|
||||
}
|
||||
dockerVersion, err := dockerClient.ServerVersion(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal("Error with Docker server.\n", err)
|
||||
}
|
||||
log.Printf("Docker API version: %s, %s\n", dockerVersion.APIVersion, dockerVersion.Version)
|
||||
|
||||
metricsOptions := metrics.BuildMetricsOptions()
|
||||
metrics.RegisterMetrics(metricsOptions)
|
||||
|
||||
var faasHandlers handlerSet
|
||||
|
||||
if config.UseExternalProvider() {
|
||||
|
||||
reverseProxy := httputil.NewSingleHostReverseProxy(config.FunctionsProviderURL)
|
||||
|
||||
faasHandlers.Proxy = handler(reverseProxy)
|
||||
faasHandlers.RoutelessProxy = handler(reverseProxy)
|
||||
faasHandlers.Alert = handler(reverseProxy)
|
||||
faasHandlers.ListFunctions = handler(reverseProxy)
|
||||
faasHandlers.DeployFunction = handler(reverseProxy)
|
||||
faasHandlers.DeleteFunction = handler(reverseProxy)
|
||||
|
||||
} else {
|
||||
faasHandlers.Proxy = internalHandlers.MakeProxy(metricsOptions, true, dockerClient, &logger)
|
||||
faasHandlers.RoutelessProxy = internalHandlers.MakeProxy(metricsOptions, true, dockerClient, &logger)
|
||||
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.
|
||||
functionLabel := "function"
|
||||
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)
|
||||
|
||||
functionHandler := faasHandlers.MakeProxy(metricsOptions, true, dockerClient, &logger)
|
||||
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))
|
||||
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")
|
||||
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.MakeProxy(metricsOptions, false, dockerClient, &logger)).Methods("POST")
|
||||
r.HandleFunc("/", faasHandlers.RoutelessProxy).Methods("POST")
|
||||
|
||||
metricsHandler := metrics.PrometheusHandler()
|
||||
r.Handle("/metrics", metricsHandler)
|
||||
|
||||
// This could exist in a separate process - records the replicas of each swarm service.
|
||||
functionLabel := "function"
|
||||
metrics.AttachSwarmWatcher(dockerClient, metricsOptions, functionLabel)
|
||||
|
||||
r.Handle("/", http.RedirectHandler("/ui/", http.StatusMovedPermanently)).Methods("GET")
|
||||
|
||||
tcpPort := 8080
|
||||
@ -79,3 +115,10 @@ func main() {
|
||||
|
||||
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"
|
||||
"time"
|
||||
|
||||
gateway "github.com/alexellis/faas/gateway"
|
||||
"github.com/alexellis/faas/gateway/types"
|
||||
)
|
||||
|
||||
type EnvBucket struct {
|
||||
@ -28,9 +28,21 @@ func (e EnvBucket) Setenv(key string, value string) {
|
||||
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) {
|
||||
defaults := NewEnvBucket()
|
||||
readConfig := gateway.ReadConfig{}
|
||||
readConfig := types.ReadConfig{}
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
|
||||
@ -49,7 +61,7 @@ func TestRead_ReadAndWriteTimeoutConfig(t *testing.T) {
|
||||
defaults.Setenv("read_timeout", "10")
|
||||
defaults.Setenv("write_timeout", "60")
|
||||
|
||||
readConfig := gateway.ReadConfig{}
|
||||
readConfig := types.ReadConfig{}
|
||||
config := readConfig.Read(defaults)
|
||||
|
||||
if (config.ReadTimeout) != time.Duration(10)*time.Second {
|
||||
|
@ -1,6 +1,8 @@
|
||||
package main
|
||||
package types
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
@ -51,11 +53,25 @@ func (ReadConfig) Read(hasEnv HasEnv) GatewayConfig {
|
||||
cfg.ReadTimeout = time.Duration(readTimeout) * 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
|
||||
}
|
||||
|
||||
// GatewayConfig for the process.
|
||||
type GatewayConfig struct {
|
||||
ReadTimeout time.Duration
|
||||
WriteTimeout time.Duration
|
||||
ReadTimeout 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