Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for gRPC #2223

Merged
merged 2 commits into from
Mar 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 1 addition & 1 deletion images/nginx/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.

# 0.0.0 shouldn't clobber any released builds
TAG ?= 0.37
TAG ?= 0.38
REGISTRY ?= quay.io/kubernetes-ingress-controller
ARCH ?= $(shell go env GOARCH)
DOCKER ?= gcloud docker --
Expand Down
8 changes: 4 additions & 4 deletions images/nginx/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ set -o errexit
set -o nounset
set -o pipefail

export NGINX_VERSION=1.13.9
export NGINX_VERSION=1.13.10
export NDK_VERSION=0.3.0
export VTS_VERSION=0.1.15
export SETMISC_VERSION=0.31
Expand All @@ -28,7 +28,7 @@ export MORE_HEADERS_VERSION=0.33
export NGINX_DIGEST_AUTH=274490cec649e7300fea97fed13d84e596bbc0ce
export NGINX_SUBSTITUTIONS=bc58cb11844bc42735bbaef7085ea86ace46d05b
export NGINX_OPENTRACING_VERSION=0.2.1
export OPENTRACING_CPP_VERSION=1.2.0
export OPENTRACING_CPP_VERSION=1.3.0
export ZIPKIN_CPP_VERSION=0.2.0
export JAEGER_VERSION=0.2.0
export MODSECURITY_VERSION=1.0.0
Expand Down Expand Up @@ -112,7 +112,7 @@ mkdir --verbose -p "$BUILD_PATH"
cd "$BUILD_PATH"

# download, verify and extract the source files
get_src 5faea18857516fe68d30be39c3032bd22ed9cf85e1a6fdf32e3721d96ff7fa42 \
get_src 336182104d90be3c40c874f7f06f87dbb357da1dc74ea573ad081a0f29a94885 \
"http://nginx.org/download/nginx-$NGINX_VERSION.tar.gz"

get_src 88e05a99a8a7419066f5ae75966fb1efc409bad4522d14986da074554ae61619 \
Expand All @@ -139,7 +139,7 @@ get_src 618551948ab14cac51d6e4ad00452312c7b09938f59ebff4f93875013be31f2d \
get_src ce66acf943a604ef9a0bb477c7efca1fe583076991647aa646aa3d8804328364 \
"https://github.com/opentracing-contrib/nginx-opentracing/archive/v$NGINX_OPENTRACING_VERSION.tar.gz"

get_src c77041cb2f147ac81b2b0702abfced5565a9cebc318d045c060a4c3e074009ee \
get_src 06dc5f9740d27dc4684399e491211be46a8069a10277f25513dadeb71199ce4c \
"https://github.com/opentracing/opentracing-cpp/archive/v$OPENTRACING_CPP_VERSION.tar.gz"

get_src 611eb6a1ff1c326c472421ae2486ba34a94ddc78d90047df3f097bcdad3298e3 \
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"`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aledbf logs part must be a typo, should not that be grpc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zaa yes. I just opened a PR to fix this

}

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