Add support for gRPC (#2223)

* Update nginx to 1.13.10 and enable gRPC

* Add support for grpc
This commit is contained in:
Manuel Alejandro de Brito Fontes 2018-03-22 00:38:47 -03:00 committed by GitHub
parent 2b5d4d7928
commit adf12fced1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 151 additions and 6 deletions

View file

@ -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

View file

@ -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),
}, },
} }
} }

View 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)
}

View 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")
}
}

View file

@ -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
} }
} }
} }

View file

@ -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

View file

@ -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

View file

@ -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
} }