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)
|
||||
|
||||
# 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)
|
||||
QEMUARCH=arm
|
||||
|
|
|
@ -32,6 +32,7 @@ import (
|
|||
"k8s.io/ingress-nginx/internal/ingress/annotations/connection"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/cors"
|
||||
"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/ipwhitelist"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/loadbalancing"
|
||||
|
@ -91,6 +92,7 @@ type Ingress struct {
|
|||
XForwardedPrefix bool
|
||||
SSLCiphers string
|
||||
Logs log.Config
|
||||
GRPC bool
|
||||
}
|
||||
|
||||
// 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),
|
||||
"SSLCiphers": sslcipher.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.Connection = anns.Connection
|
||||
loc.Logs = anns.Logs
|
||||
loc.GRPC = anns.GRPC
|
||||
|
||||
if loc.Redirect.FromToWWW {
|
||||
server.RedirectFromToWWW = true
|
||||
|
@ -489,6 +490,7 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
|||
UsePortInRedirects: anns.UsePortInRedirects,
|
||||
Connection: anns.Connection,
|
||||
Logs: anns.Logs,
|
||||
GRPC: anns.GRPC,
|
||||
}
|
||||
|
||||
if loc.Redirect.FromToWWW {
|
||||
|
@ -923,6 +925,7 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
|||
defLoc.VtsFilterKey = anns.VtsFilterKey
|
||||
defLoc.Whitelist = anns.Whitelist
|
||||
defLoc.Denied = anns.Denied
|
||||
defLoc.GRPC = anns.GRPC
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -324,6 +324,12 @@ func buildProxyPass(host string, b interface{}, loc interface{}, dynamicConfigur
|
|||
path := location.Path
|
||||
proto := "http"
|
||||
|
||||
proxyPass := "proxy_pass"
|
||||
if location.GRPC {
|
||||
proxyPass = "grpc_pass"
|
||||
proto = "grpc"
|
||||
}
|
||||
|
||||
upstreamName := "upstream_balancer"
|
||||
|
||||
if !dynamicConfigurationEnabled {
|
||||
|
@ -334,6 +340,9 @@ func buildProxyPass(host string, b interface{}, loc interface{}, dynamicConfigur
|
|||
if backend.Name == location.Backend {
|
||||
if backend.Secure || backend.SSLPassthrough {
|
||||
proto = "https"
|
||||
if location.GRPC {
|
||||
proto = "grpcs"
|
||||
}
|
||||
}
|
||||
|
||||
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 := 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 path == location.Rewrite.Target {
|
||||
|
@ -382,14 +391,14 @@ func buildProxyPass(host string, b interface{}, loc interface{}, dynamicConfigur
|
|||
return fmt.Sprintf(`
|
||||
rewrite %s(.*) /$1 break;
|
||||
rewrite %s / break;
|
||||
%vproxy_pass %s://%s;
|
||||
%v`, path, location.Path, xForwardedPrefix, proto, upstreamName, abu)
|
||||
%v%v %s://%s;
|
||||
%v`, path, location.Path, xForwardedPrefix, proxyPass, proto, upstreamName, abu)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`
|
||||
rewrite %s(.*) %s/$1 break;
|
||||
%vproxy_pass %s://%s;
|
||||
%v`, path, location.Rewrite.Target, xForwardedPrefix, proto, upstreamName, abu)
|
||||
%v%v %s://%s;
|
||||
%v`, path, location.Rewrite.Target, xForwardedPrefix, proxyPass, proto, upstreamName, abu)
|
||||
}
|
||||
|
||||
// default proxy_pass
|
||||
|
|
|
@ -265,6 +265,9 @@ type Location struct {
|
|||
// Logs allows to enable or disable the nginx logs
|
||||
// By default this is enabled
|
||||
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
|
||||
|
|
|
@ -382,6 +382,9 @@ func (l1 *Location) Equal(l2 *Location) bool {
|
|||
if !(&l1.Logs).Equal(&l2.Logs) {
|
||||
return false
|
||||
}
|
||||
if l1.GRPC != l2.GRPC {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue