faas/gateway/types/readconfig.go
Alex Ellis (OpenFaaS Ltd) a7d486eee6 Make OpenFaaS CE use the provider for load-balancing
This change removes the direct functions option which was
used originally for Docker Swarm. The Community Edition will
rely on the faas provider - faas-netes / faasd for load-balancing
of requests.

Direct Functions is required in order to delegate load-balancing
to Istio, Linkerd or some other kind of service mesh.

Tested by deploying a modified gateway image to a KinD cluster,
deploying the env function, and scaling to two replicas. This
balanced the load between the two pods by printing out the names
and then I ran a test with hey which returned 200s for all the
requests.

The prober which was part of the Istio support is no longer
required in the CE gateway so is removed for simplicity.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alex@openfaas.com>
2023-01-11 12:12:26 +00:00

242 lines
6.5 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 (
"fmt"
"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 gateway server configuration from environmental variables
func (ReadConfig) Read(hasEnv HasEnv) (*GatewayConfig, error) {
cfg := GatewayConfig{
PrometheusHost: "prometheus",
PrometheusPort: 9090,
}
defaultDuration := time.Second * 60
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 {
return nil, fmt.Errorf("if functions_provider_url is provided, then it should be a valid URL, error: %s", err)
}
}
if len(hasEnv.Getenv("logs_provider_url")) > 0 {
var err error
cfg.LogsProviderURL, err = url.Parse(hasEnv.Getenv("logs_provider_url"))
if err != nil {
return nil, fmt.Errorf("if logs_provider_url is provided, then it should be a valid URL, error: %s", err)
}
} else if cfg.FunctionsProviderURL != nil {
cfg.LogsProviderURL, _ = url.Parse(cfg.FunctionsProviderURL.String())
}
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 {
return nil, fmt.Errorf("faas_nats_port invalid number: %s", faasNATSPort)
}
}
faasNATSClusterName := hasEnv.Getenv("faas_nats_cluster_name")
if len(faasNATSClusterName) > 0 {
cfg.NATSClusterName = &faasNATSClusterName
} else {
v := "faas-cluster"
cfg.NATSClusterName = &v
}
faasNATSChannel := hasEnv.Getenv("faas_nats_channel")
if len(faasNATSChannel) > 0 {
cfg.NATSChannel = &faasNATSChannel
} else {
v := "faas-request"
cfg.NATSChannel = &v
}
prometheusPort := hasEnv.Getenv("faas_prometheus_port")
if len(prometheusPort) > 0 {
prometheusPortVal, err := strconv.Atoi(prometheusPort)
if err != nil {
return nil, fmt.Errorf("faas_prometheus_port invalid number: %s", faasNATSPort)
}
cfg.PrometheusPort = prometheusPortVal
}
prometheusHost := hasEnv.Getenv("faas_prometheus_host")
if len(prometheusHost) > 0 {
cfg.PrometheusHost = prometheusHost
}
cfg.UseBasicAuth = parseBoolValue(hasEnv.Getenv("basic_auth"))
secretPath := hasEnv.Getenv("secret_mount_path")
if len(secretPath) == 0 {
secretPath = "/run/secrets/"
}
cfg.SecretMountPath = secretPath
cfg.ScaleFromZero = parseBoolValue(hasEnv.Getenv("scale_from_zero"))
cfg.MaxIdleConns = 1024
cfg.MaxIdleConnsPerHost = 1024
maxIdleConns := hasEnv.Getenv("max_idle_conns")
if len(maxIdleConns) > 0 {
val, err := strconv.Atoi(maxIdleConns)
if err != nil {
return nil, fmt.Errorf("invalid value for max_idle_conns: %s", maxIdleConns)
}
cfg.MaxIdleConns = val
}
maxIdleConnsPerHost := hasEnv.Getenv("max_idle_conns_per_host")
if len(maxIdleConnsPerHost) > 0 {
val, err := strconv.Atoi(maxIdleConnsPerHost)
if err != nil {
return nil, fmt.Errorf("invalid value for max_idle_conns_per_host: %s", maxIdleConnsPerHost)
}
cfg.MaxIdleConnsPerHost = val
}
cfg.AuthProxyURL = hasEnv.Getenv("auth_proxy_url")
cfg.AuthProxyPassBody = parseBoolValue(hasEnv.Getenv("auth_proxy_pass_body"))
cfg.Namespace = hasEnv.Getenv("function_namespace")
return &cfg, nil
}
// GatewayConfig provides config for the API Gateway server 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
// URL for alternate function logs provider.
LogsProviderURL *url.URL
// Address of the NATS service. Required for async mode.
NATSAddress *string
// Port of the NATS Service. Required for async mode.
NATSPort *int
// The name of the NATS Streaming cluster. Required for async mode.
NATSClusterName *string
// NATSChannel is the name of the NATS Streaming channel used for asynchronous function invocations.
NATSChannel *string
// Host to connect to Prometheus.
PrometheusHost string
// Port to connect to Prometheus.
PrometheusPort int
// If set, reads secrets from file-system for enabling basic auth.
UseBasicAuth bool
// SecretMountPath specifies where to read secrets from for embedded basic auth
SecretMountPath string
// Enable the gateway to scale any service from 0 replicas to its configured "min replicas"
ScaleFromZero bool
// MaxIdleConns with a default value of 1024, can be used for tuning HTTP proxy performance
MaxIdleConns int
// MaxIdleConnsPerHost with a default value of 1024, can be used for tuning HTTP proxy performance
MaxIdleConnsPerHost int
// AuthProxyURL specifies URL for an authenticating proxy, disabled when blank, enabled when valid URL i.e. http://basic-auth.openfaas:8080/validate
AuthProxyURL string
// AuthProxyPassBody pass body to validation proxy
AuthProxyPassBody bool
// Namespace for endpoints
Namespace string
}
// UseNATS Use NATSor not
func (g *GatewayConfig) UseNATS() bool {
return g.NATSPort != nil &&
g.NATSAddress != nil
}
// UseExternalProvider is now required for all providers
func (g *GatewayConfig) UseExternalProvider() bool {
return g.FunctionsProviderURL != nil
}