faas/gateway/types/readconfig.go
Alex Ellis 0c7e59fe8a Add direct_functions mode to gateway for tuning
Adds a pair of configuration options for performance tuning. The
gateway can now invoke functions directly and can bypass the
provider. See updated table in README.md for configuration values.

BaseURLResolver is added with unit tests that decouples resolving
upstream URL from the reverse proxy client code.

- SingleHostBaseURLResolver resolves a single upstream host
- FunctionAsHostBaseURLResolver resolves host based upon conventions
within the URL of the request to a function for direct access

Tested with Kubernetes (faas-netes) and faas-swarm through UI, CLI
calling system endpoints and functions directly.

Signed-off-by: Alex Ellis (VMware) <alexellis2@gmail.com>
2018-03-23 16:35:37 +00:00

155 lines
3.8 KiB
Go

// 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 types
import (
"log"
"net/url"
"os"
"strconv"
"time"
)
// OsEnv implements interface to wrap os.Getenv
type OsEnv struct {
}
// Getenv wraps os.Getenv
func (OsEnv) Getenv(key string) string {
return os.Getenv(key)
}
// HasEnv provides interface for os.Getenv
type HasEnv interface {
Getenv(key string) string
}
// ReadConfig constitutes config from env variables
type ReadConfig struct {
}
func parseBoolValue(val string) bool {
if val == "true" {
return true
}
return false
}
func parseIntOrDurationValue(val string, fallback time.Duration) time.Duration {
if len(val) > 0 {
parsedVal, parseErr := strconv.Atoi(val)
if parseErr == nil && parsedVal >= 0 {
return time.Duration(parsedVal) * time.Second
}
}
duration, durationErr := time.ParseDuration(val)
if durationErr != nil {
return fallback
}
return duration
}
// Read fetches config from environmental variables.
func (ReadConfig) Read(hasEnv HasEnv) GatewayConfig {
cfg := GatewayConfig{
PrometheusHost: "prometheus",
PrometheusPort: 9090,
}
defaultDuration := time.Second * 8
cfg.ReadTimeout = parseIntOrDurationValue(hasEnv.Getenv("read_timeout"), defaultDuration)
cfg.WriteTimeout = parseIntOrDurationValue(hasEnv.Getenv("write_timeout"), defaultDuration)
cfg.UpstreamTimeout = parseIntOrDurationValue(hasEnv.Getenv("upstream_timeout"), defaultDuration)
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)
}
}
faasNATSAddress := hasEnv.Getenv("faas_nats_address")
if len(faasNATSAddress) > 0 {
cfg.NATSAddress = &faasNATSAddress
}
faasNATSPort := hasEnv.Getenv("faas_nats_port")
if len(faasNATSPort) > 0 {
port, err := strconv.Atoi(faasNATSPort)
if err == nil {
cfg.NATSPort = &port
} else {
log.Println("faas_nats_port invalid number: " + faasNATSPort)
}
}
prometheusPort := hasEnv.Getenv("faas_prometheus_port")
if len(prometheusPort) > 0 {
prometheusPortVal, err := strconv.Atoi(prometheusPort)
if err != nil {
log.Println("Invalid port for faas_prometheus_port")
} else {
cfg.PrometheusPort = prometheusPortVal
}
}
prometheusHost := hasEnv.Getenv("faas_prometheus_host")
if len(prometheusHost) > 0 {
cfg.PrometheusHost = prometheusHost
}
cfg.DirectFunctions = parseBoolValue(hasEnv.Getenv("direct_functions"))
cfg.DirectFunctionsSuffix = hasEnv.Getenv("direct_functions_suffix")
return cfg
}
// GatewayConfig for the process.
type GatewayConfig struct {
// HTTP timeout for reading a request from clients.
ReadTimeout time.Duration
// HTTP timeout for writing a response from functions.
WriteTimeout time.Duration
// UpstreamTimeout maximum duration of HTTP call to upstream URL
UpstreamTimeout time.Duration
// URL for alternate functions provider.
FunctionsProviderURL *url.URL
// Address of the NATS service. Required for async mode.
NATSAddress *string
// Port of the NATS Service. Required for async mode.
NATSPort *int
// Host to connect to Prometheus.
PrometheusHost string
// Port to connect to Prometheus.
PrometheusPort int
// If set to true we will access upstream functions directly rather than through the upstream provider
DirectFunctions bool
// If set this will be used to resolve functions directly
DirectFunctionsSuffix string
}
// UseNATS Use NATSor not
func (g *GatewayConfig) UseNATS() bool {
return g.NATSPort != nil &&
g.NATSAddress != nil
}
// UseExternalProvider decide whether to bypass built-in Docker Swarm engine
func (g *GatewayConfig) UseExternalProvider() bool {
return g.FunctionsProviderURL != nil
}