Merge pull request #344 from aledbf/tcp-udp

Refactoring of TCP and UDP services
This commit is contained in:
Manuel Alejandro de Brito Fontes 2017-02-24 20:48:13 -03:00 committed by GitHub
commit f509d5b44f
7 changed files with 71 additions and 116 deletions

View file

@ -299,8 +299,8 @@ type TemplateConfig struct {
Backends []*ingress.Backend Backends []*ingress.Backend
PassthroughBackends []*ingress.SSLPassthroughBackend PassthroughBackends []*ingress.SSLPassthroughBackend
Servers []*ingress.Server Servers []*ingress.Server
TCPBackends []*ingress.Location TCPBackends []ingress.L4Service
UDPBackends []*ingress.Location UDPBackends []ingress.L4Service
HealthzURI string HealthzURI string
CustomErrors bool CustomErrors bool
Cfg Configuration Cfg Configuration

View file

@ -134,7 +134,6 @@ var (
"buildSSLPassthroughUpstreams": buildSSLPassthroughUpstreams, "buildSSLPassthroughUpstreams": buildSSLPassthroughUpstreams,
"buildResolvers": buildResolvers, "buildResolvers": buildResolvers,
"isLocationAllowed": isLocationAllowed, "isLocationAllowed": isLocationAllowed,
"buildStreamUpstreams": buildStreamUpstreams,
"contains": strings.Contains, "contains": strings.Contains,
"hasPrefix": strings.HasPrefix, "hasPrefix": strings.HasPrefix,
@ -193,34 +192,6 @@ func buildSSLPassthroughUpstreams(b interface{}, sslb interface{}) string {
return buf.String() return buf.String()
} }
func buildStreamUpstreams(proto string, b interface{}, s interface{}) string {
backends := b.([]*ingress.Backend)
streams := s.([]*ingress.Location)
buf := bytes.NewBuffer(make([]byte, 0, 10))
// multiple services can use the same upstream.
// avoid duplications using a map[name]=true
u := make(map[string]bool)
for _, stream := range streams {
if u[stream.Backend] {
continue
}
u[stream.Backend] = true
fmt.Fprintf(buf, "upstream %v-%v {\n", proto, stream.Backend)
// TODO: find a better way to avoid empty stream upstreams
fmt.Fprintf(buf, "\t\tserver 127.0.0.1:8181 down;\n")
for _, backend := range backends {
if backend.Name == stream.Backend {
for _, server := range backend.Endpoints {
fmt.Fprintf(buf, "\t\tserver %v:%v;\n", server.Address, server.Port)
}
break
}
}
fmt.Fprint(buf, "\t}\n\n")
}
return buf.String()
}
// buildLocation produces the location string, if the ingress has redirects // buildLocation produces the location string, if the ingress has redirects
// (specified through the ingress.kubernetes.io/rewrite-to annotation) // (specified through the ingress.kubernetes.io/rewrite-to annotation)
func buildLocation(input interface{}) string { func buildLocation(input interface{}) string {

View file

@ -326,8 +326,8 @@ http {
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_cookie_domain {{ $location.Proxy.CookiePath }}; proxy_cookie_domain {{ $location.Proxy.CookieDomain }};
proxy_cookie_path {{ $location.Proxy.CookieDomain }}; proxy_cookie_path {{ $location.Proxy.CookiePath }};
{{/* rewrite only works if the content is not compressed */}} {{/* rewrite only works if the content is not compressed */}}
{{ if $location.Redirect.AddBaseURL }} {{ if $location.Redirect.AddBaseURL }}
@ -460,24 +460,32 @@ stream {
ssl_preread on; ssl_preread on;
} }
{{ buildStreamUpstreams "tcp" $backends .TCPBackends }}
{{ buildStreamUpstreams "udp" $backends .UDPBackends }}
# TCP services # TCP services
{{ range $i, $tcpServer := .TCPBackends }} {{ range $i, $tcpServer := .TCPBackends }}
upstream {{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }} {
{{ range $j, $endpoint := $tcpServer.Endpoints }}
server {{ $endpoint.Address }}:{{ $endpoint.Port }};
{{ end }}
}
server { server {
listen {{ $tcpServer.Path }}; listen {{ $tcpServer.Port }};
proxy_pass tcp-{{ $tcpServer.Backend }}; proxy_pass {{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }};
} }
{{ end }} {{ end }}
# UDP services # UDP services
{{ range $i, $udpServer := .UDPBackends }} {{ range $i, $udpServer := .UDPBackends }}
upstream {{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }} {
{{ range $j, $endpoint := $udpServer.Endpoints }}
server {{ $endpoint.Address }}:{{ $endpoint.Port }};
{{ end }}
}
server { server {
listen {{ $udpServer.Path }} udp; listen {{ $udpServer.Port }};
proxy_responses 1; proxy_responses 1;
proxy_pass udp-{{ $udpServer.Backend }}; proxy_pass {{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }};
} }
{{ end }} {{ end }}
} }

View file

@ -57134,57 +57134,7 @@
}] }]
}], }],
"sslDHParam": "", "sslDHParam": "",
"tcpBackends": [{ "tcpBackends": [],
"path": "2222",
"isDefBackend": false,
"backend": "default-echoheaders-2222",
"basicDigestAuth": {
"type": "",
"realm": "",
"file": "",
"secured": false
},
"externalAuth": {
"url": "",
"method": "",
"sendBody": false
},
"rateLimit": {
"connections": {
"name": "",
"limit": 0,
"burst": 0,
"sharedSize": 0
},
"rps": {
"name": "",
"limit": 0,
"burst": 0,
"sharedSize": 0
}
},
"redirect": {
"target": "",
"addBaseUrl": false,
"sslRedirect": false
},
"whitelist": {
"cidr": null
},
"proxy": {
"conectTimeout": 0,
"sendTimeout": 0,
"readTimeout": 0,
"bufferSize": ""
},
"certificateAuth": {
"secret": "",
"certFilename": "",
"keyFilename": "",
"caFilename": "",
"pemSha": ""
}
}],
"udpBackends": [], "udpBackends": [],
"backends": [{ "backends": [{
"name": "default-echoheaders-80", "name": "default-echoheaders-80",

View file

@ -40,8 +40,8 @@ type Configuration struct {
SendTimeout int `json:"sendTimeout"` SendTimeout int `json:"sendTimeout"`
ReadTimeout int `json:"readTimeout"` ReadTimeout int `json:"readTimeout"`
BufferSize string `json:"bufferSize"` BufferSize string `json:"bufferSize"`
CookieDomain string `json:"proxyCookieDomain"` CookieDomain string `json:"cookieDomain"`
CookiePath string `json:"proxyCookiePath"` CookiePath string `json:"cookiePath"`
} }
type proxy struct { type proxy struct {
@ -83,8 +83,8 @@ func (a proxy) Parse(ing *extensions.Ingress) (interface{}, error) {
} }
cd, err := parser.GetStringAnnotation(cookieDomain, ing) cd, err := parser.GetStringAnnotation(cookieDomain, ing)
if err != nil || cp == "" { if err != nil || cd == "" {
cp = defBackend.ProxyCookieDomain cd = defBackend.ProxyCookieDomain
} }
bs, err := parser.GetStringAnnotation(bodySize, ing) bs, err := parser.GetStringAnnotation(bodySize, ing)
@ -92,6 +92,5 @@ func (a proxy) Parse(ing *extensions.Ingress) (interface{}, error) {
bs = defBackend.ProxyBodySize bs = defBackend.ProxyBodySize
} }
return &Configuration{bs, ct, st, rt, bufs, return &Configuration{bs, ct, st, rt, bufs, cd, cp}, nil
cd, cp}, nil
} }

View file

@ -426,30 +426,30 @@ func (ic *GenericController) sync(key interface{}) error {
return nil return nil
} }
func (ic *GenericController) getStreamServices(configmapName string, proto api.Protocol) []*ingress.Location { func (ic *GenericController) getStreamServices(configmapName string, proto api.Protocol) []ingress.L4Service {
glog.V(3).Infof("obtaining information about stream services of type %v located in configmap %v", proto, configmapName) glog.V(3).Infof("obtaining information about stream services of type %v located in configmap %v", proto, configmapName)
if configmapName == "" { if configmapName == "" {
// no configmap configured // no configmap configured
return []*ingress.Location{} return []ingress.L4Service{}
} }
ns, name, err := k8s.ParseNameNS(configmapName) ns, name, err := k8s.ParseNameNS(configmapName)
if err != nil { if err != nil {
glog.Errorf("unexpected error reading configmap %v: %v", name, err) glog.Errorf("unexpected error reading configmap %v: %v", name, err)
return []*ingress.Location{} return []ingress.L4Service{}
} }
configmap, err := ic.getConfigMap(ns, name) configmap, err := ic.getConfigMap(ns, name)
if err != nil { if err != nil {
glog.Errorf("unexpected error reading configmap %v: %v", name, err) glog.Errorf("unexpected error reading configmap %v: %v", name, err)
return []*ingress.Location{} return []ingress.L4Service{}
} }
var svcs []*ingress.Location var svcs []ingress.L4Service
// k -> port to expose // k -> port to expose
// v -> <namespace>/<service name>:<port from service to be used> // v -> <namespace>/<service name>:<port from service to be used>
for k, v := range configmap.Data { for k, v := range configmap.Data {
_, err := strconv.Atoi(k) externalPort, err := strconv.Atoi(k)
if err != nil { if err != nil {
glog.Warningf("%v is not valid as a TCP/UDP port", k) glog.Warningf("%v is not valid as a TCP/UDP port", k)
continue continue
@ -517,9 +517,15 @@ func (ic *GenericController) getStreamServices(configmapName string, proto api.P
continue continue
} }
svcs = append(svcs, &ingress.Location{ svcs = append(svcs, ingress.L4Service{
Path: k, Port: externalPort,
Backend: fmt.Sprintf("%v-%v-%v", svcNs, svcName, svcPort), Backend: ingress.L4Backend{
Name: svcName,
Namespace: svcNs,
Port: intstr.FromString(svcPort),
Protocol: proto,
},
Endpoints: endps,
}) })
} }
@ -817,6 +823,8 @@ func (ic *GenericController) createServers(data []interface{},
SendTimeout: bdef.ProxySendTimeout, SendTimeout: bdef.ProxySendTimeout,
ReadTimeout: bdef.ProxyReadTimeout, ReadTimeout: bdef.ProxyReadTimeout,
BufferSize: bdef.ProxyBufferSize, BufferSize: bdef.ProxyBufferSize,
CookieDomain: bdef.ProxyCookieDomain,
CookiePath: bdef.ProxyCookiePath,
} }
// This adds the Default Certificate to Default Backend and also for vhosts missing the secret // This adds the Default Certificate to Default Backend and also for vhosts missing the secret

View file

@ -22,6 +22,7 @@ import (
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/cache" "k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/healthz" "k8s.io/kubernetes/pkg/healthz"
"k8s.io/kubernetes/pkg/util/intstr"
cache_store "k8s.io/ingress/core/pkg/cache" cache_store "k8s.io/ingress/core/pkg/cache"
"k8s.io/ingress/core/pkg/ingress/annotations/auth" "k8s.io/ingress/core/pkg/ingress/annotations/auth"
@ -132,10 +133,10 @@ type Configuration struct {
Servers []*Server `json:"servers"` Servers []*Server `json:"servers"`
// TCPEndpoints contain endpoints for tcp streams handled by this backend // TCPEndpoints contain endpoints for tcp streams handled by this backend
// +optional // +optional
TCPEndpoints []*Location `json:"tcpEndpoints,omitempty"` TCPEndpoints []L4Service `json:"tcpEndpoints,omitempty"`
// UDPEndpoints contain endpoints for udp streams handled by this backend // UDPEndpoints contain endpoints for udp streams handled by this backend
// +optional // +optional
UDPEndpoints []*Location `json:"udpEndpoints,omitempty"` UDPEndpoints []L4Service `json:"udpEndpoints,omitempty"`
// PassthroughBackend contains the backends used for SSL passthrough. // PassthroughBackend contains the backends used for SSL passthrough.
// It contains information about the associated Server Name Indication (SNI). // It contains information about the associated Server Name Indication (SNI).
// +optional // +optional
@ -292,3 +293,21 @@ type SSLPassthroughBackend struct {
// Hostname returns the FQDN of the server // Hostname returns the FQDN of the server
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
} }
// L4Service describes a L4 Ingress service.
type L4Service struct {
// Port external port to expose
Port int `json:"port"`
// Backend of the service
Backend L4Backend `json:"backend"`
// Endpoints active endpoints of the service
Endpoints []Endpoint `json:"endpoins"`
}
// L4Backend describes the kubernetes service behind L4 Ingress service
type L4Backend struct {
Port intstr.IntOrString `json:"port"`
Name string `json:"name"`
Namespace string `json:"namespace"`
Protocol api.Protocol `json:"protocol"`
}