Add support for gRPC (#2223)
* Update nginx to 1.13.10 and enable gRPC * Add support for grpc
This commit is contained in:
parent
2b5d4d7928
commit
adf12fced1
8 changed files with 151 additions and 6 deletions
2
Makefile
2
Makefile
|
@ -50,7 +50,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.37
|
BASEIMAGE?=quay.io/kubernetes-ingress-controller/nginx-$(ARCH):0.38
|
||||||
|
|
||||||
ifeq ($(ARCH),arm)
|
ifeq ($(ARCH),arm)
|
||||||
QEMUARCH=arm
|
QEMUARCH=arm
|
||||||
|
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"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/defaultbackend"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/defaultbackend"
|
||||||
|
"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/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"
|
||||||
|
@ -91,6 +92,7 @@ type Ingress struct {
|
||||||
XForwardedPrefix bool
|
XForwardedPrefix bool
|
||||||
SSLCiphers string
|
SSLCiphers string
|
||||||
Logs log.Config
|
Logs log.Config
|
||||||
|
GRPC bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -130,6 +132,7 @@ func NewAnnotationExtractor(cfg resolver.Resolver) Extractor {
|
||||||
"XForwardedPrefix": xforwardedprefix.NewParser(cfg),
|
"XForwardedPrefix": xforwardedprefix.NewParser(cfg),
|
||||||
"SSLCiphers": sslcipher.NewParser(cfg),
|
"SSLCiphers": sslcipher.NewParser(cfg),
|
||||||
"Logs": log.NewParser(cfg),
|
"Logs": log.NewParser(cfg),
|
||||||
|
"GRPC": grpc.NewParser(cfg),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
44
internal/ingress/annotations/grpc/main.go
Normal file
44
internal/ingress/annotations/grpc/main.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 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 grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
|
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
type grpc struct {
|
||||||
|
r resolver.Resolver
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewParser creates a new gRPC annotation parser
|
||||||
|
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
|
||||||
|
return grpc{r}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAnnotations parses the annotations contained in the ingress
|
||||||
|
// rule used to indicate if the Kubernetes service exposes gRPC
|
||||||
|
func (a grpc) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
|
if ing.GetAnnotations() == nil {
|
||||||
|
return false, ing_errors.ErrMissingAnnotations
|
||||||
|
}
|
||||||
|
|
||||||
|
return parser.GetBoolAnnotation("grpc-backend", ing)
|
||||||
|
}
|
80
internal/ingress/annotations/grpc/main_test.go
Normal file
80
internal/ingress/annotations/grpc/main_test.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 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 grpc
|
||||||
|
|
||||||
|
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/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildIngress() *extensions.Ingress {
|
||||||
|
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),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseAnnotations(t *testing.T) {
|
||||||
|
ing := buildIngress()
|
||||||
|
|
||||||
|
_, err := NewParser(&resolver.Mock{}).Parse(ing)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := map[string]string{}
|
||||||
|
data[parser.GetAnnotationWithPrefix("grpc-backend")] = "true"
|
||||||
|
ing.SetAnnotations(data)
|
||||||
|
// test ingress using the annotation without a TLS section
|
||||||
|
_, err = NewParser(&resolver.Mock{}).Parse(ing)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error parsing ingress with sslpassthrough")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test with a valid host
|
||||||
|
ing.Spec.TLS = []extensions.IngressTLS{
|
||||||
|
{
|
||||||
|
Hosts: []string{"foo.bar.com"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
i, err := NewParser(&resolver.Mock{}).Parse(ing)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected error parsing ingress with sslpassthrough")
|
||||||
|
}
|
||||||
|
val, ok := i.(bool)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("expected a bool type")
|
||||||
|
}
|
||||||
|
if !val {
|
||||||
|
t.Errorf("expected true but false returned")
|
||||||
|
}
|
||||||
|
}
|
|
@ -455,6 +455,7 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
||||||
loc.UsePortInRedirects = anns.UsePortInRedirects
|
loc.UsePortInRedirects = anns.UsePortInRedirects
|
||||||
loc.Connection = anns.Connection
|
loc.Connection = anns.Connection
|
||||||
loc.Logs = anns.Logs
|
loc.Logs = anns.Logs
|
||||||
|
loc.GRPC = anns.GRPC
|
||||||
|
|
||||||
if loc.Redirect.FromToWWW {
|
if loc.Redirect.FromToWWW {
|
||||||
server.RedirectFromToWWW = true
|
server.RedirectFromToWWW = true
|
||||||
|
@ -489,6 +490,7 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
||||||
UsePortInRedirects: anns.UsePortInRedirects,
|
UsePortInRedirects: anns.UsePortInRedirects,
|
||||||
Connection: anns.Connection,
|
Connection: anns.Connection,
|
||||||
Logs: anns.Logs,
|
Logs: anns.Logs,
|
||||||
|
GRPC: anns.GRPC,
|
||||||
}
|
}
|
||||||
|
|
||||||
if loc.Redirect.FromToWWW {
|
if loc.Redirect.FromToWWW {
|
||||||
|
@ -923,6 +925,7 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
||||||
defLoc.VtsFilterKey = anns.VtsFilterKey
|
defLoc.VtsFilterKey = anns.VtsFilterKey
|
||||||
defLoc.Whitelist = anns.Whitelist
|
defLoc.Whitelist = anns.Whitelist
|
||||||
defLoc.Denied = anns.Denied
|
defLoc.Denied = anns.Denied
|
||||||
|
defLoc.GRPC = anns.GRPC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,6 +324,12 @@ func buildProxyPass(host string, b interface{}, loc interface{}, dynamicConfigur
|
||||||
path := location.Path
|
path := location.Path
|
||||||
proto := "http"
|
proto := "http"
|
||||||
|
|
||||||
|
proxyPass := "proxy_pass"
|
||||||
|
if location.GRPC {
|
||||||
|
proxyPass = "grpc_pass"
|
||||||
|
proto = "grpc"
|
||||||
|
}
|
||||||
|
|
||||||
upstreamName := "upstream_balancer"
|
upstreamName := "upstream_balancer"
|
||||||
|
|
||||||
if !dynamicConfigurationEnabled {
|
if !dynamicConfigurationEnabled {
|
||||||
|
@ -334,6 +340,9 @@ func buildProxyPass(host string, b interface{}, loc interface{}, dynamicConfigur
|
||||||
if backend.Name == location.Backend {
|
if backend.Name == location.Backend {
|
||||||
if backend.Secure || backend.SSLPassthrough {
|
if backend.Secure || backend.SSLPassthrough {
|
||||||
proto = "https"
|
proto = "https"
|
||||||
|
if location.GRPC {
|
||||||
|
proto = "grpcs"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !dynamicConfigurationEnabled && isSticky(host, location, backend.SessionAffinity.CookieSessionAffinity.Locations) {
|
if !dynamicConfigurationEnabled && isSticky(host, location, backend.SessionAffinity.CookieSessionAffinity.Locations) {
|
||||||
|
@ -345,7 +354,7 @@ func buildProxyPass(host string, b interface{}, loc interface{}, dynamicConfigur
|
||||||
}
|
}
|
||||||
|
|
||||||
// defProxyPass returns the default proxy_pass, just the name of the upstream
|
// defProxyPass returns the default proxy_pass, just the name of the upstream
|
||||||
defProxyPass := fmt.Sprintf("proxy_pass %s://%s;", proto, upstreamName)
|
defProxyPass := fmt.Sprintf("%v %s://%s;", proxyPass, proto, upstreamName)
|
||||||
|
|
||||||
// if the path in the ingress rule is equals to the target: no special rewrite
|
// if the path in the ingress rule is equals to the target: no special rewrite
|
||||||
if path == location.Rewrite.Target {
|
if path == location.Rewrite.Target {
|
||||||
|
@ -382,14 +391,14 @@ func buildProxyPass(host string, b interface{}, loc interface{}, dynamicConfigur
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
rewrite %s(.*) /$1 break;
|
rewrite %s(.*) /$1 break;
|
||||||
rewrite %s / break;
|
rewrite %s / break;
|
||||||
%vproxy_pass %s://%s;
|
%v%v %s://%s;
|
||||||
%v`, path, location.Path, xForwardedPrefix, proto, upstreamName, abu)
|
%v`, path, location.Path, xForwardedPrefix, proxyPass, proto, upstreamName, abu)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
rewrite %s(.*) %s/$1 break;
|
rewrite %s(.*) %s/$1 break;
|
||||||
%vproxy_pass %s://%s;
|
%v%v %s://%s;
|
||||||
%v`, path, location.Rewrite.Target, xForwardedPrefix, proto, upstreamName, abu)
|
%v`, path, location.Rewrite.Target, xForwardedPrefix, proxyPass, proto, upstreamName, abu)
|
||||||
}
|
}
|
||||||
|
|
||||||
// default proxy_pass
|
// default proxy_pass
|
||||||
|
|
|
@ -265,6 +265,9 @@ type Location struct {
|
||||||
// Logs allows to enable or disable the nginx logs
|
// Logs allows to enable or disable the nginx logs
|
||||||
// By default this is enabled
|
// By default this is enabled
|
||||||
Logs log.Config `json:"logs,omitempty"`
|
Logs log.Config `json:"logs,omitempty"`
|
||||||
|
// GRPC indicates if the kubernetes service exposes a gRPC interface
|
||||||
|
// By default this is false
|
||||||
|
GRPC bool `json:"logs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSLPassthroughBackend describes a SSL upstream server configured
|
// SSLPassthroughBackend describes a SSL upstream server configured
|
||||||
|
|
|
@ -382,6 +382,9 @@ func (l1 *Location) Equal(l2 *Location) bool {
|
||||||
if !(&l1.Logs).Equal(&l2.Logs) {
|
if !(&l1.Logs).Equal(&l2.Logs) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if l1.GRPC != l2.GRPC {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue