Skip to content

Commit

Permalink
Add support for gRPC (#2223)
Browse files Browse the repository at this point in the history
* Update nginx to 1.13.10 and enable gRPC

* Add support for grpc
  • Loading branch information
aledbf authored Mar 22, 2018
1 parent 2b5d4d7 commit adf12fc
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions internal/ingress/annotations/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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),
},
}
}
Expand Down
44 changes: 44 additions & 0 deletions internal/ingress/annotations/grpc/main.go
Original file line number Diff line number Diff line change
@@ -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 changes: 80 additions & 0 deletions internal/ingress/annotations/grpc/main_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
3 changes: 3 additions & 0 deletions internal/ingress/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
}
}
Expand Down
19 changes: 14 additions & 5 deletions internal/ingress/controller/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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) {
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions internal/ingress/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions internal/ingress/types_equals.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down

0 comments on commit adf12fc

Please sign in to comment.