Annotations for the InfluxDB Module
Signed-off-by: Lorenzo Fontana <lo@linux.com>
This commit is contained in:
parent
83ef18fdb8
commit
1fdab5a18a
10 changed files with 256 additions and 1 deletions
2
Makefile
2
Makefile
|
@ -53,7 +53,7 @@ IMAGE = $(REGISTRY)/$(IMGNAME)
|
||||||
MULTI_ARCH_IMG = $(IMAGE)-$(ARCH)
|
MULTI_ARCH_IMG = $(IMAGE)-$(ARCH)
|
||||||
|
|
||||||
# Set default base image dynamically for each arch
|
# Set default base image dynamically for each arch
|
||||||
BASEIMAGE?=quay.io/kubernetes-ingress-controller/nginx-$(ARCH):0.45
|
BASEIMAGE?=quay.io/kubernetes-ingress-controller/nginx-$(ARCH):0.46
|
||||||
|
|
||||||
ifeq ($(ARCH),arm)
|
ifeq ($(ARCH),arm)
|
||||||
QEMUARCH=arm
|
QEMUARCH=arm
|
||||||
|
|
|
@ -77,6 +77,11 @@ You can add these Kubernetes annotations to specific Ingress objects to customiz
|
||||||
|[nginx.ingress.kubernetes.io/lua-resty-waf-debug](#lua-resty-waf)|"true" or "false"|
|
|[nginx.ingress.kubernetes.io/lua-resty-waf-debug](#lua-resty-waf)|"true" or "false"|
|
||||||
|[nginx.ingress.kubernetes.io/lua-resty-waf-ignore-rulesets](#lua-resty-waf)|string|
|
|[nginx.ingress.kubernetes.io/lua-resty-waf-ignore-rulesets](#lua-resty-waf)|string|
|
||||||
|[nginx.ingress.kubernetes.io/lua-resty-waf-extra-rules](#lua-resty-waf)|string|
|
|[nginx.ingress.kubernetes.io/lua-resty-waf-extra-rules](#lua-resty-waf)|string|
|
||||||
|
|[nginx.ingress.kubernetes.io/enable-influxdb](#influxdb)|"true" or "false"|
|
||||||
|
|[nginx.ingress.kubernetes.io/influxdb-measurement](#influxdb)|string|
|
||||||
|
|[nginx.ingress.kubernetes.io/influxdb-port](#influxdb)|string|
|
||||||
|
|[nginx.ingress.kubernetes.io/influxdb-host](#influxdb)|string|
|
||||||
|
|[nginx.ingress.kubernetes.io/influxdb-server-name](#influxdb)|string|
|
||||||
|
|
||||||
### Rewrite
|
### Rewrite
|
||||||
|
|
||||||
|
@ -553,3 +558,25 @@ Additionally, if the gRPC service requires TLS, add `nginx.ingress.kubernetes.io
|
||||||
Exposing a gRPC service using HTTP is not supported.
|
Exposing a gRPC service using HTTP is not supported.
|
||||||
|
|
||||||
[configmap]: ./configmap.md
|
[configmap]: ./configmap.md
|
||||||
|
|
||||||
|
### InfluxDB
|
||||||
|
|
||||||
|
Using `influxdb-*` annotations we can monitor requests passing through a Location by sending them to an InfluxDB backend exposing the UDP socket
|
||||||
|
using the [nginx-influxdb-module](https://github.com/influxdata/nginx-influxdb-module/).
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
nginx.ingress.kubernetes.io/enable-influxdb: "true"
|
||||||
|
nginx.ingress.kubernetes.io/influxdb-measurement: "nginx-reqs"
|
||||||
|
nginx.ingress.kubernetes.io/influxdb-port: "8089"
|
||||||
|
nginx.ingress.kubernetes.io/influxdb-host: "influxdb"
|
||||||
|
nginx.ingress.kubernetes.io/influxdb-server-name: "nginx-ingress"
|
||||||
|
```
|
||||||
|
|
||||||
|
For the `influxdb-host` parameter you have two options:
|
||||||
|
|
||||||
|
To use the module in the Kubernetes Nginx ingress controller, you have two options:
|
||||||
|
|
||||||
|
- Use an InfluxDB server configured to enable the [UDP protocol](https://docs.influxdata.com/influxdb/v1.5/supported_protocols/udp/).
|
||||||
|
- Deploy Telegraf as a sidecar proxy to the Ingress controller configured to listen UDP with the [socket listener input](https://github.com/influxdata/telegraf/tree/release-1.6/plugins/inputs/socket_listener) and to write using
|
||||||
|
anyone of the [outputs plugins](https://github.com/influxdata/telegraf/tree/release-1.6/plugins/outputs)
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ import (
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/defaultbackend"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/defaultbackend"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/grpc"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/grpc"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/healthcheck"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/healthcheck"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/influxdb"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/ipwhitelist"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/ipwhitelist"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/loadbalancing"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/loadbalancing"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/log"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/log"
|
||||||
|
@ -95,6 +96,7 @@ type Ingress struct {
|
||||||
Logs log.Config
|
Logs log.Config
|
||||||
GRPC bool
|
GRPC bool
|
||||||
LuaRestyWAF luarestywaf.Config
|
LuaRestyWAF luarestywaf.Config
|
||||||
|
InfluxDB influxdb.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extractor defines the annotation parsers to be used in the extraction of annotations
|
// Extractor defines the annotation parsers to be used in the extraction of annotations
|
||||||
|
@ -136,6 +138,7 @@ func NewAnnotationExtractor(cfg resolver.Resolver) Extractor {
|
||||||
"Logs": log.NewParser(cfg),
|
"Logs": log.NewParser(cfg),
|
||||||
"GRPC": grpc.NewParser(cfg),
|
"GRPC": grpc.NewParser(cfg),
|
||||||
"LuaRestyWAF": luarestywaf.NewParser(cfg),
|
"LuaRestyWAF": luarestywaf.NewParser(cfg),
|
||||||
|
"InfluxDB": influxdb.NewParser(cfg),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
104
internal/ingress/annotations/influxdb/main.go
Normal file
104
internal/ingress/annotations/influxdb/main.go
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package influxdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
type influxdb struct {
|
||||||
|
r resolver.Resolver
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config contains the IfluxDB configuration to be used in the Ingress
|
||||||
|
type Config struct {
|
||||||
|
InfluxDBEnabled bool `json:"influxDBEnabled"`
|
||||||
|
InfluxDBMeasurement string `json:"influxDBMeasurement"`
|
||||||
|
InfluxDBPort string `json:"influxDBPort"`
|
||||||
|
InfluxDBHost string `json:"influxDBHost"`
|
||||||
|
InfluxDBServerName string `json:"influxDBServerName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewParser creates a new InfluxDB annotation parser
|
||||||
|
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
|
||||||
|
return influxdb{r}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses the annotations to look for InfluxDB configurations
|
||||||
|
func (c influxdb) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
|
influxdbEnabled, err := parser.GetBoolAnnotation("enable-influxdb", ing)
|
||||||
|
if err != nil {
|
||||||
|
influxdbEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
influxdbMeasurement, err := parser.GetStringAnnotation("influxdb-measurement", ing)
|
||||||
|
if err != nil {
|
||||||
|
influxdbMeasurement = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
influxdbPort, err := parser.GetStringAnnotation("influxdb-port", ing)
|
||||||
|
if err != nil {
|
||||||
|
// This is not the default 8086 port but the port usually used to expose
|
||||||
|
// influxdb in UDP, the module uses UDP to talk to influx via the line protocol.
|
||||||
|
influxdbPort = "8089"
|
||||||
|
}
|
||||||
|
|
||||||
|
influxdbHost, err := parser.GetStringAnnotation("influxdb-host", ing)
|
||||||
|
if err != nil {
|
||||||
|
influxdbHost = "127.0.0.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
influxdbServerName, err := parser.GetStringAnnotation("influxdb-server-name", ing)
|
||||||
|
if err != nil {
|
||||||
|
influxdbServerName = "nginx-ingress"
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Config{
|
||||||
|
InfluxDBEnabled: influxdbEnabled,
|
||||||
|
InfluxDBMeasurement: influxdbMeasurement,
|
||||||
|
InfluxDBPort: influxdbPort,
|
||||||
|
InfluxDBHost: influxdbHost,
|
||||||
|
InfluxDBServerName: influxdbServerName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal tests for equality between two Config types
|
||||||
|
func (e1 *Config) Equal(e2 *Config) bool {
|
||||||
|
if e1 == e2 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if e1 == nil || e2 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if e1.InfluxDBEnabled != e2.InfluxDBEnabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if e1.InfluxDBPort != e2.InfluxDBPort {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if e1.InfluxDBHost != e2.InfluxDBHost {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if e1.InfluxDBServerName != e2.InfluxDBServerName {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
101
internal/ingress/annotations/influxdb/main_test.go
Normal file
101
internal/ingress/annotations/influxdb/main_test.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package influxdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
api "k8s.io/api/core/v1"
|
||||||
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildIngress() *extensions.Ingress {
|
||||||
|
defaultBackend := extensions.IngressBackend{
|
||||||
|
ServiceName: "default-backend",
|
||||||
|
ServicePort: intstr.FromInt(80),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &extensions.Ingress{
|
||||||
|
ObjectMeta: meta_v1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
},
|
||||||
|
Spec: extensions.IngressSpec{
|
||||||
|
Backend: &extensions.IngressBackend{
|
||||||
|
ServiceName: "default-backend",
|
||||||
|
ServicePort: intstr.FromInt(80),
|
||||||
|
},
|
||||||
|
Rules: []extensions.IngressRule{
|
||||||
|
{
|
||||||
|
Host: "foo.bar.com",
|
||||||
|
IngressRuleValue: extensions.IngressRuleValue{
|
||||||
|
HTTP: &extensions.HTTPIngressRuleValue{
|
||||||
|
Paths: []extensions.HTTPIngressPath{
|
||||||
|
{
|
||||||
|
Path: "/foo",
|
||||||
|
Backend: defaultBackend,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIngressInfluxDB(t *testing.T) {
|
||||||
|
ing := buildIngress()
|
||||||
|
|
||||||
|
data := map[string]string{}
|
||||||
|
data[parser.GetAnnotationWithPrefix("enable-influxdb")] = "true"
|
||||||
|
data[parser.GetAnnotationWithPrefix("influxdb-measurement")] = "nginxmeasures"
|
||||||
|
data[parser.GetAnnotationWithPrefix("influxdb-port")] = "9091"
|
||||||
|
data[parser.GetAnnotationWithPrefix("influxdb-host")] = "mytelegrafserver.mycompany.mytld"
|
||||||
|
data[parser.GetAnnotationWithPrefix("influxdb-server-name")] = "nginx-test-1"
|
||||||
|
ing.SetAnnotations(data)
|
||||||
|
|
||||||
|
influx, _ := NewParser(&resolver.Mock{}).Parse(ing)
|
||||||
|
nginxInflux, ok := influx.(*Config)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("expected a Config type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !nginxInflux.InfluxDBEnabled {
|
||||||
|
t.Errorf("expected influxdb enabled but returned %v", nginxInflux.InfluxDBEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
if nginxInflux.InfluxDBMeasurement != "nginxmeasures" {
|
||||||
|
t.Errorf("expected measurement name not found. Found %v", nginxInflux.InfluxDBMeasurement)
|
||||||
|
}
|
||||||
|
|
||||||
|
if nginxInflux.InfluxDBPort != "9091" {
|
||||||
|
t.Errorf("expected port not found. Found %v", nginxInflux.InfluxDBPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
if nginxInflux.InfluxDBHost != "mytelegrafserver.mycompany.mytld" {
|
||||||
|
t.Errorf("expected host not found. Found %v", nginxInflux.InfluxDBHost)
|
||||||
|
}
|
||||||
|
|
||||||
|
if nginxInflux.InfluxDBServerName != "nginx-test-1" {
|
||||||
|
t.Errorf("expected server name not found. Found %v", nginxInflux.InfluxDBServerName)
|
||||||
|
}
|
||||||
|
}
|
|
@ -516,6 +516,11 @@ type Configuration struct {
|
||||||
// DisableLuaRestyWAF disables lua-resty-waf globally regardless
|
// DisableLuaRestyWAF disables lua-resty-waf globally regardless
|
||||||
// of whether there's an ingress that has enabled the WAF using annotation
|
// of whether there's an ingress that has enabled the WAF using annotation
|
||||||
DisableLuaRestyWAF bool `json:"disable-lua-resty-waf"`
|
DisableLuaRestyWAF bool `json:"disable-lua-resty-waf"`
|
||||||
|
|
||||||
|
// EnableInfluxDB enables the nginx InfluxDB extension
|
||||||
|
// http://github.com/influxdata/nginx-influxdb-module/
|
||||||
|
// By default this is disabled
|
||||||
|
EnableInfluxDB bool `json:"enable-influxdb"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDefault returns the default nginx configuration
|
// NewDefault returns the default nginx configuration
|
||||||
|
|
|
@ -450,6 +450,7 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
||||||
loc.Logs = anns.Logs
|
loc.Logs = anns.Logs
|
||||||
loc.GRPC = anns.GRPC
|
loc.GRPC = anns.GRPC
|
||||||
loc.LuaRestyWAF = anns.LuaRestyWAF
|
loc.LuaRestyWAF = anns.LuaRestyWAF
|
||||||
|
loc.InfluxDB = anns.InfluxDB
|
||||||
|
|
||||||
if loc.Redirect.FromToWWW {
|
if loc.Redirect.FromToWWW {
|
||||||
server.RedirectFromToWWW = true
|
server.RedirectFromToWWW = true
|
||||||
|
@ -486,6 +487,7 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
||||||
Logs: anns.Logs,
|
Logs: anns.Logs,
|
||||||
GRPC: anns.GRPC,
|
GRPC: anns.GRPC,
|
||||||
LuaRestyWAF: anns.LuaRestyWAF,
|
LuaRestyWAF: anns.LuaRestyWAF,
|
||||||
|
InfluxDB: anns.InfluxDB,
|
||||||
}
|
}
|
||||||
|
|
||||||
if loc.Redirect.FromToWWW {
|
if loc.Redirect.FromToWWW {
|
||||||
|
@ -922,6 +924,7 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
||||||
defLoc.Denied = anns.Denied
|
defLoc.Denied = anns.Denied
|
||||||
defLoc.GRPC = anns.GRPC
|
defLoc.GRPC = anns.GRPC
|
||||||
defLoc.LuaRestyWAF = anns.LuaRestyWAF
|
defLoc.LuaRestyWAF = anns.LuaRestyWAF
|
||||||
|
defLoc.InfluxDB = anns.InfluxDB
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/authtls"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/authtls"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/connection"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/connection"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/cors"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/cors"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/influxdb"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/ipwhitelist"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/ipwhitelist"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/log"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/log"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/luarestywaf"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/luarestywaf"
|
||||||
|
@ -271,6 +272,9 @@ type Location struct {
|
||||||
GRPC bool `json:"grpc"`
|
GRPC bool `json:"grpc"`
|
||||||
// LuaRestyWAF contains parameters to configure lua-resty-waf
|
// LuaRestyWAF contains parameters to configure lua-resty-waf
|
||||||
LuaRestyWAF luarestywaf.Config `json:"luaRestyWAF"`
|
LuaRestyWAF luarestywaf.Config `json:"luaRestyWAF"`
|
||||||
|
// InfluxDB allows to monitor the incoming request by sending them to an influxdb database
|
||||||
|
// +optional
|
||||||
|
InfluxDB influxdb.Config `json:"influxDB,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSLPassthroughBackend describes a SSL upstream server configured
|
// SSLPassthroughBackend describes a SSL upstream server configured
|
||||||
|
|
|
@ -389,6 +389,10 @@ func (l1 *Location) Equal(l2 *Location) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !(&l1.InfluxDB).Equal(&l2.InfluxDB) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -980,6 +980,10 @@ stream {
|
||||||
{{ template "CORS" $location }}
|
{{ template "CORS" $location }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if $location.InfluxDB.InfluxDBEnabled }}
|
||||||
|
influxdb server_name=$location.InfluxDB.InfluxDBServerName host=$location.InfluxDB.InfluxDBHost port=$location.InfluxDB.InfluxDBPort measurement=$location.InfluxDB.InfluxDBMeasurement enabled=true;
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
{{ if not (empty $location.Redirect.URL) }}
|
{{ if not (empty $location.Redirect.URL) }}
|
||||||
if ($uri ~* {{ $path }}) {
|
if ($uri ~* {{ $path }}) {
|
||||||
return {{ $location.Redirect.Code }} {{ $location.Redirect.URL }};
|
return {{ $location.Redirect.Code }} {{ $location.Redirect.URL }};
|
||||||
|
|
Loading…
Reference in a new issue