mirror of
https://github.com/openfaas/faas.git
synced 2025-06-22 06:43:23 +00:00
Add feature for invoking namespaced functions
When coupled with the latest version of faas-netes, the gateway can now invoke, query and deploy functions into alternative namespaces. Tested e2e by creating a namespace "fn" and deploying, then invoking a function deployed there and in the default namespace. Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
This commit is contained in:
committed by
Alex Ellis
parent
0a90125aba
commit
238ce1be23
@ -4,10 +4,11 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -52,7 +53,7 @@ func parseIntOrDurationValue(val string, fallback time.Duration) time.Duration {
|
||||
}
|
||||
|
||||
// Read fetches gateway server configuration from environmental variables
|
||||
func (ReadConfig) Read(hasEnv HasEnv) GatewayConfig {
|
||||
func (ReadConfig) Read(hasEnv HasEnv) (*GatewayConfig, error) {
|
||||
cfg := GatewayConfig{
|
||||
PrometheusHost: "prometheus",
|
||||
PrometheusPort: 9090,
|
||||
@ -68,7 +69,7 @@ func (ReadConfig) Read(hasEnv HasEnv) GatewayConfig {
|
||||
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 nil, fmt.Errorf("if functions_provider_url is provided, then it should be a valid URL, error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,7 +77,7 @@ func (ReadConfig) Read(hasEnv HasEnv) GatewayConfig {
|
||||
var err error
|
||||
cfg.LogsProviderURL, err = url.Parse(hasEnv.Getenv("logs_provider_url"))
|
||||
if err != nil {
|
||||
log.Fatal("If logs_provider_url is provided, then it should be a valid URL.", err)
|
||||
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())
|
||||
@ -93,7 +94,7 @@ func (ReadConfig) Read(hasEnv HasEnv) GatewayConfig {
|
||||
if err == nil {
|
||||
cfg.NATSPort = &port
|
||||
} else {
|
||||
log.Println("faas_nats_port invalid number: " + faasNATSPort)
|
||||
return nil, fmt.Errorf("faas_nats_port invalid number: %s", faasNATSPort)
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,10 +102,10 @@ func (ReadConfig) Read(hasEnv HasEnv) GatewayConfig {
|
||||
if len(prometheusPort) > 0 {
|
||||
prometheusPortVal, err := strconv.Atoi(prometheusPort)
|
||||
if err != nil {
|
||||
log.Println("Invalid port for faas_prometheus_port")
|
||||
} else {
|
||||
cfg.PrometheusPort = prometheusPortVal
|
||||
return nil, fmt.Errorf("faas_prometheus_port invalid number: %s", faasNATSPort)
|
||||
}
|
||||
cfg.PrometheusPort = prometheusPortVal
|
||||
|
||||
}
|
||||
|
||||
prometheusHost := hasEnv.Getenv("faas_prometheus_host")
|
||||
@ -131,26 +132,34 @@ func (ReadConfig) Read(hasEnv HasEnv) GatewayConfig {
|
||||
if len(maxIdleConns) > 0 {
|
||||
val, err := strconv.Atoi(maxIdleConns)
|
||||
if err != nil {
|
||||
log.Println("Invalid value for max_idle_conns")
|
||||
} else {
|
||||
cfg.MaxIdleConns = val
|
||||
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 {
|
||||
log.Println("Invalid value for max_idle_conns_per_host")
|
||||
} else {
|
||||
cfg.MaxIdleConnsPerHost = val
|
||||
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"))
|
||||
|
||||
return cfg
|
||||
cfg.Namespace = hasEnv.Getenv("function_namespace")
|
||||
|
||||
if len(cfg.DirectFunctionsSuffix) > 0 && len(cfg.Namespace) > 0 {
|
||||
if strings.HasPrefix(cfg.DirectFunctionsSuffix, cfg.Namespace) == false {
|
||||
return nil, fmt.Errorf("function_namespace must be a sub-string of direct_functions_suffix")
|
||||
}
|
||||
}
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// GatewayConfig provides config for the API Gateway server process
|
||||
@ -209,6 +218,9 @@ type GatewayConfig struct {
|
||||
|
||||
// AuthProxyPassBody pass body to validation proxy
|
||||
AuthProxyPassBody bool
|
||||
|
||||
// Namespace for endpoints
|
||||
Namespace string
|
||||
}
|
||||
|
||||
// UseNATS Use NATSor not
|
||||
|
@ -31,7 +31,7 @@ func TestRead_UseExternalProvider_Defaults(t *testing.T) {
|
||||
defaults := NewEnvBucket()
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if config.UseExternalProvider() != false {
|
||||
t.Log("Default for UseExternalProvider should be false")
|
||||
@ -44,9 +44,77 @@ func TestRead_UseExternalProvider_Defaults(t *testing.T) {
|
||||
}
|
||||
|
||||
if len(config.DirectFunctionsSuffix) > 0 {
|
||||
t.Log("Default for DirectFunctionsSuffix should be empty as a default")
|
||||
t.Log("Default for DirectFunctionsSuffix should be empty")
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if len(config.Namespace) > 0 {
|
||||
t.Log("Default for Namespace should be empty")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestRead_NamespaceOverride(t *testing.T) {
|
||||
|
||||
defaults := NewEnvBucket()
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
defaults.Setenv("function_namespace", "fn")
|
||||
wantSuffix := "fn"
|
||||
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if config.Namespace != wantSuffix {
|
||||
t.Logf("Namespace want: %s, got: %s", wantSuffix, config.Namespace)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestRead_NamespaceOverrideAgressWithFunctionSuffix_Valid(t *testing.T) {
|
||||
|
||||
defaults := NewEnvBucket()
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
defaults.Setenv("direct_functions", "true")
|
||||
wantSuffix := "openfaas-fn.cluster.local.svc."
|
||||
|
||||
defaults.Setenv("direct_functions_suffix", wantSuffix)
|
||||
defaults.Setenv("function_namespace", "openfaas-fn")
|
||||
|
||||
_, err := readConfig.Read(defaults)
|
||||
|
||||
if err != nil {
|
||||
t.Logf("Error found: %s", err)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestRead_NamespaceOverrideAgressWithFunctionSuffix_Invalid(t *testing.T) {
|
||||
|
||||
defaults := NewEnvBucket()
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
defaults.Setenv("direct_functions", "true")
|
||||
wantSuffix := "openfaas-fn.cluster.local.svc."
|
||||
|
||||
defaults.Setenv("direct_functions_suffix", wantSuffix)
|
||||
defaults.Setenv("function_namespace", "fn")
|
||||
|
||||
_, err := readConfig.Read(defaults)
|
||||
|
||||
if err == nil {
|
||||
t.Logf("Expected an error because function_namespace should be a sub-string of direct_functions_suffix")
|
||||
t.Fail()
|
||||
return
|
||||
}
|
||||
|
||||
want := "function_namespace must be a sub-string of direct_functions_suffix"
|
||||
|
||||
if want != err.Error() {
|
||||
t.Logf("Error want: %s, got: %s", want, err.Error())
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRead_DirectFunctionsOverride(t *testing.T) {
|
||||
@ -56,7 +124,7 @@ func TestRead_DirectFunctionsOverride(t *testing.T) {
|
||||
wantSuffix := "openfaas-fn.cluster.local.svc."
|
||||
defaults.Setenv("direct_functions_suffix", wantSuffix)
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if config.DirectFunctions != true {
|
||||
t.Logf("DirectFunctions should be true, got: %v", config.DirectFunctions)
|
||||
@ -73,7 +141,7 @@ func TestRead_ScaleZeroDefaultAndOverride(t *testing.T) {
|
||||
defaults := NewEnvBucket()
|
||||
readConfig := ReadConfig{}
|
||||
// defaults.Setenv("scale_from_zero", "true")
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
want := false
|
||||
if config.ScaleFromZero != want {
|
||||
@ -82,7 +150,7 @@ func TestRead_ScaleZeroDefaultAndOverride(t *testing.T) {
|
||||
}
|
||||
|
||||
defaults.Setenv("scale_from_zero", "true")
|
||||
config = readConfig.Read(defaults)
|
||||
config, _ = readConfig.Read(defaults)
|
||||
want = true
|
||||
|
||||
if config.ScaleFromZero != want {
|
||||
@ -96,7 +164,7 @@ func TestRead_EmptyTimeoutConfig(t *testing.T) {
|
||||
defaults := NewEnvBucket()
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if (config.ReadTimeout) != time.Duration(8)*time.Second {
|
||||
t.Log("ReadTimeout incorrect")
|
||||
@ -114,7 +182,7 @@ func TestRead_ReadAndWriteTimeoutConfig(t *testing.T) {
|
||||
defaults.Setenv("write_timeout", "60")
|
||||
|
||||
readConfig := ReadConfig{}
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if (config.ReadTimeout) != time.Duration(10)*time.Second {
|
||||
t.Logf("ReadTimeout incorrect, got: %d\n", config.ReadTimeout)
|
||||
@ -132,7 +200,7 @@ func TestRead_ReadAndWriteTimeoutDurationConfig(t *testing.T) {
|
||||
defaults.Setenv("write_timeout", "1m30s")
|
||||
|
||||
readConfig := ReadConfig{}
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if (config.ReadTimeout) != time.Duration(20)*time.Second {
|
||||
t.Logf("ReadTimeout incorrect, got: %d\n", config.ReadTimeout)
|
||||
@ -148,7 +216,7 @@ func TestRead_UseNATSDefaultsToOff(t *testing.T) {
|
||||
defaults := NewEnvBucket()
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if config.UseNATS() == true {
|
||||
t.Log("NATS is supposed to be off by default")
|
||||
@ -162,7 +230,7 @@ func TestRead_UseNATS(t *testing.T) {
|
||||
defaults.Setenv("faas_nats_port", "6222")
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if config.UseNATS() == false {
|
||||
t.Log("NATS was requested in config, but not enabled.")
|
||||
@ -177,12 +245,17 @@ func TestRead_UseNATSBadPort(t *testing.T) {
|
||||
defaults.Setenv("faas_nats_port", "6fff")
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
_, err := readConfig.Read(defaults)
|
||||
|
||||
if config.UseNATS() == true {
|
||||
t.Log("NATS had bad config, should not be enabled.")
|
||||
t.Fail()
|
||||
if err != nil {
|
||||
want := "faas_nats_port invalid number: 6fff"
|
||||
|
||||
if want != err.Error() {
|
||||
t.Errorf("want error: %q, got: %q", want, err.Error())
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRead_PrometheusNonDefaults(t *testing.T) {
|
||||
@ -191,7 +264,7 @@ func TestRead_PrometheusNonDefaults(t *testing.T) {
|
||||
defaults.Setenv("faas_prometheus_port", "9999")
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if config.PrometheusHost != "prom1" {
|
||||
t.Logf("config.PrometheusHost, want: %s, got: %s\n", "prom1", config.PrometheusHost)
|
||||
@ -209,7 +282,7 @@ func TestRead_PrometheusDefaults(t *testing.T) {
|
||||
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if config.PrometheusHost != "prometheus" {
|
||||
t.Logf("config.PrometheusHost, want: %s, got: %s\n", "prometheus", config.PrometheusHost)
|
||||
@ -227,7 +300,7 @@ func TestRead_BasicAuthDefaults(t *testing.T) {
|
||||
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if config.UseBasicAuth != false {
|
||||
t.Logf("config.UseBasicAuth, want: %t, got: %t\n", false, config.UseBasicAuth)
|
||||
@ -248,7 +321,7 @@ func TestRead_BasicAuth_SetTrue(t *testing.T) {
|
||||
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if config.UseBasicAuth != true {
|
||||
t.Logf("config.UseBasicAuth, want: %t, got: %t\n", true, config.UseBasicAuth)
|
||||
@ -267,7 +340,7 @@ func TestRead_MaxIdleConnsDefaults(t *testing.T) {
|
||||
|
||||
readConfig := ReadConfig{}
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if config.MaxIdleConns != 1024 {
|
||||
t.Logf("config.MaxIdleConns, want: %d, got: %d\n", 1024, config.MaxIdleConns)
|
||||
@ -287,7 +360,7 @@ func TestRead_MaxIdleConns_Override(t *testing.T) {
|
||||
defaults.Setenv("max_idle_conns", fmt.Sprintf("%d", 100))
|
||||
defaults.Setenv("max_idle_conns_per_host", fmt.Sprintf("%d", 2))
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if config.MaxIdleConns != 100 {
|
||||
t.Logf("config.MaxIdleConns, want: %d, got: %d\n", 100, config.MaxIdleConns)
|
||||
@ -307,7 +380,7 @@ func TestRead_AuthProxy_Defaults(t *testing.T) {
|
||||
wantURL := ""
|
||||
wantBody := false
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if config.AuthProxyPassBody != wantBody {
|
||||
t.Logf("config.AuthProxyPassBody, want: %t, got: %t\n", wantBody, config.AuthProxyPassBody)
|
||||
@ -329,7 +402,7 @@ func TestRead_AuthProxy_DefaultsOverrides(t *testing.T) {
|
||||
defaults.Setenv("auth_proxy_url", wantURL)
|
||||
defaults.Setenv("auth_proxy_pass_body", fmt.Sprintf("%t", wantBody))
|
||||
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
|
||||
if config.AuthProxyPassBody != wantBody {
|
||||
t.Logf("config.AuthProxyPassBody, want: %t, got: %t\n", wantBody, config.AuthProxyPassBody)
|
||||
@ -347,7 +420,7 @@ func TestRead_LogsProviderURL(t *testing.T) {
|
||||
|
||||
t.Run("default value is nil when functions_provider_url is empty", func(t *testing.T) {
|
||||
readConfig := ReadConfig{}
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
if config.LogsProviderURL != nil {
|
||||
t.Fatalf("config.LogsProviderURL, want: %s, got: %s\n", "", config.LogsProviderURL)
|
||||
}
|
||||
@ -358,7 +431,7 @@ func TestRead_LogsProviderURL(t *testing.T) {
|
||||
defaults.Setenv("functions_provider_url", expected)
|
||||
|
||||
readConfig := ReadConfig{}
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
if config.LogsProviderURL.String() != expected {
|
||||
t.Fatalf("config.LogsProviderURL, want: %s, got: %s\n", expected, config.LogsProviderURL)
|
||||
}
|
||||
@ -369,7 +442,7 @@ func TestRead_LogsProviderURL(t *testing.T) {
|
||||
defaults.Setenv("logs_provider_url", expected)
|
||||
|
||||
readConfig := ReadConfig{}
|
||||
config := readConfig.Read(defaults)
|
||||
config, _ := readConfig.Read(defaults)
|
||||
if config.LogsProviderURL.String() != expected {
|
||||
t.Fatalf("config.LogsProviderURL, want: %s, got: %s\n", expected, config.LogsProviderURL)
|
||||
}
|
||||
|
Reference in New Issue
Block a user