From 8dee3c7d0a5fafbe7920b9d5e8111c7ee158942f Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 24 Jul 2023 16:43:48 +0200 Subject: [PATCH] predicates: add Config predicate E.g. it could be used to let skipper instance know in which zone it is deployed: ```yaml containers: - env: - name: TOPOLOGY_ZONE valueFrom: fieldRef: fieldPath: metadata.annotations['topology.kubernetes.io/zone'] args: - skipper - -config-predicate-values=zone=$(TOPOLOGY_ZONE) - ... ``` such that it only enables specific route for that zone: ``` za: Path("/foo") && Config("zone", "a") -> "http://backend.zone-a.test"; zb: Path("/foo") && Config("zone", "b") -> "http://backend.zone-b.test"; zc: Path("/foo") && Config("zone", "c") -> "http://backend.zone-c.test"; ``` Signed-off-by: Alexander Yastrebov --- config/config.go | 3 +++ predicates/predicates.go | 1 + predicates/primitive/config.go | 49 ++++++++++++++++++++++++++++++++++ skipper.go | 4 +++ 4 files changed, 57 insertions(+) create mode 100644 predicates/primitive/config.go diff --git a/config/config.go b/config/config.go index 5f9df1d99e..736bb12bc2 100644 --- a/config/config.go +++ b/config/config.go @@ -66,6 +66,7 @@ type Config struct { DataclientPlugins *pluginFlag `yaml:"dataclient-plugin"` MultiPlugins *pluginFlag `yaml:"multi-plugin"` CompressEncodings *listFlag `yaml:"compress-encodings"` + ConfigPredicateValues mapFlags `yaml:"config-predicate-values"` // logging, metrics, profiling, tracing: EnablePrometheusMetrics bool `yaml:"enable-prometheus-metrics"` @@ -348,6 +349,7 @@ func NewConfig() *Config { flag.Var(cfg.DataclientPlugins, "dataclient-plugin", "set a custom dataclient plugins to load, a comma separated list of name and arguments") flag.Var(cfg.MultiPlugins, "multi-plugin", "set a custom multitype plugins to load, a comma separated list of name and arguments") flag.Var(cfg.CompressEncodings, "compress-encodings", "set encodings supported for compression, the order defines priority when Accept-Header has equal quality values, see RFC 7231 section 5.3.1") + flag.Var(&cfg.ConfigPredicateValues, "config-predicate-values", "sets name-value pairs for Config predicate") // logging, metrics, tracing: flag.BoolVar(&cfg.EnablePrometheusMetrics, "enable-prometheus-metrics", false, "*Deprecated*: use metrics-flavour. Switch to Prometheus metrics format to expose metrics") @@ -704,6 +706,7 @@ func (c *Config) ToOptions() skipper.Options { Plugins: c.MultiPlugins.values, PluginDirs: []string{skipper.DefaultPluginDir}, CompressEncodings: c.CompressEncodings.values, + ConfigPredicateValues: c.ConfigPredicateValues.values, // logging, metrics, profiling, tracing: EnablePrometheusMetrics: c.EnablePrometheusMetrics, diff --git a/predicates/predicates.go b/predicates/predicates.go index f6c6024d46..1ee48de071 100644 --- a/predicates/predicates.go +++ b/predicates/predicates.go @@ -24,6 +24,7 @@ const ( TrueName = "True" FalseName = "False" ShutdownName = "Shutdown" + ConfigName = "Config" MethodName = "Method" MethodsName = "Methods" HeaderName = "Header" diff --git a/predicates/primitive/config.go b/predicates/primitive/config.go new file mode 100644 index 0000000000..024c021a05 --- /dev/null +++ b/predicates/primitive/config.go @@ -0,0 +1,49 @@ +package primitive + +import ( + "regexp" + + "github.com/zalando/skipper/predicates" + "github.com/zalando/skipper/routing" +) + +type ( + configSpec struct { + values map[string]string + } +) + +// NewConfig provides a predicate spec to create predicates +// that evaluate to true if config value matches regular expression +func NewConfig(values map[string]string) routing.PredicateSpec { + return &configSpec{values} +} + +func (*configSpec) Name() string { return predicates.ConfigName } + +func (s *configSpec) Create(args []interface{}) (routing.Predicate, error) { + if len(args) != 2 { + return nil, predicates.ErrInvalidPredicateParameters + } + + name, ok := args[0].(string) + if !ok { + return nil, predicates.ErrInvalidPredicateParameters + } + + value, ok := args[1].(string) + if !ok { + return nil, predicates.ErrInvalidPredicateParameters + } + + re, err := regexp.Compile(value) + if err != nil { + return nil, predicates.ErrInvalidPredicateParameters + } + + if re.MatchString(s.values[name]) { + return &truePredicate{}, nil + } else { + return &falsePredicate{}, nil + } +} diff --git a/skipper.go b/skipper.go index a1f9f78c7f..787c4d42bb 100644 --- a/skipper.go +++ b/skipper.go @@ -806,6 +806,9 @@ type Options struct { // CompressEncodings, if not empty replace default compression encodings CompressEncodings []string + // ConfigPredicateValues sets name-value pairs for Config predicate + ConfigPredicateValues map[string]string + // OIDCSecretsFile path to the file containing key to encrypt OpenID token OIDCSecretsFile string @@ -1798,6 +1801,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { primitive.NewTrue(), primitive.NewFalse(), primitive.NewShutdown(), + primitive.NewConfig(o.ConfigPredicateValues), pauth.NewJWTPayloadAllKV(), pauth.NewJWTPayloadAnyKV(), pauth.NewJWTPayloadAllKVRegexp(),