From 66f2e50c52a508c07072de5120c5dfab68af7d53 Mon Sep 17 00:00:00 2001 From: Winfield Chen Date: Tue, 8 Aug 2017 16:42:05 -0700 Subject: [PATCH] server-sent events support through new annotation --- controllers/nginx/configuration.md | 5 ++ .../rootfs/etc/nginx/template/nginx.tmpl | 7 +++ .../ingress/annotations/eventsource/main.go | 38 +++++++++++ .../annotations/eventsource/main_test.go | 63 +++++++++++++++++++ core/pkg/ingress/controller/annotations.go | 2 + core/pkg/ingress/controller/util_test.go | 1 + core/pkg/ingress/types.go | 3 + core/pkg/ingress/types_equals.go | 3 + 8 files changed, 122 insertions(+) create mode 100644 core/pkg/ingress/annotations/eventsource/main.go create mode 100644 core/pkg/ingress/annotations/eventsource/main_test.go diff --git a/controllers/nginx/configuration.md b/controllers/nginx/configuration.md index 101f0ee2d..611b14648 100644 --- a/controllers/nginx/configuration.md +++ b/controllers/nginx/configuration.md @@ -50,6 +50,7 @@ The following annotations are supported: |[ingress.kubernetes.io/auth-tls-verify-depth](#certificate-authentication)|number| |[ingress.kubernetes.io/configuration-snippet](#configuration-snippet)|string| |[ingress.kubernetes.io/enable-cors](#enable-cors)|true or false| +|[ingress.kubernetes.io/eventsource](#eventsource)|true or false| |[ingress.kubernetes.io/force-ssl-redirect](#server-side-https-enforcement-through-redirect)|true or false| |[ingress.kubernetes.io/limit-connections](#rate-limiting)|number| |[ingress.kubernetes.io/limit-rps](#rate-limiting)|number| @@ -228,6 +229,10 @@ If the `service-upstream` annotation is specified the following things should be * Sticky Sessions will not work as only round-robin load balancing is supported. * The `proxy_next_upstream` directive will not have any effect meaning on error the request will not be dispatched to another upstream. +### Eventsource + +The annotation `ingress.kubernetes.io/eventsource` sets headers to be compatible with Server Sent Events. + ### Server-side HTTPS enforcement through redirect By default the controller redirects (301) to `HTTPS` if TLS is enabled for that ingress. If you want to disable that behaviour globally, you can use `ssl-redirect: "false"` in the NGINX config map. diff --git a/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl b/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl index a3953e38a..d07a5e7d3 100644 --- a/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl +++ b/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl @@ -420,9 +420,16 @@ http { proxy_set_header ssl-client-cert $ssl_client_cert; {{ end }} + {{ if $location.Eventsource }} + # Allow server sent events + proxy_set_header Connection ''; + proxy_cache off; + chunked_transfer_encoding off; + {{ else }} # Allow websocket connections proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; + {{ end }} proxy_set_header X-Real-IP $the_real_ip; proxy_set_header X-Forwarded-For $the_real_ip; diff --git a/core/pkg/ingress/annotations/eventsource/main.go b/core/pkg/ingress/annotations/eventsource/main.go new file mode 100644 index 000000000..f40d823b9 --- /dev/null +++ b/core/pkg/ingress/annotations/eventsource/main.go @@ -0,0 +1,38 @@ +/* +Copyright 2017 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 eventsource + +import ( + extensions "k8s.io/api/extensions/v1beta1" + + "k8s.io/ingress/core/pkg/ingress/annotations/parser" +) + +const ( + annotation = "ingress.kubernetes.io/eventsource" +) + +type eventsource struct { +} + +func NewParser() parser.IngressAnnotation { + return eventsource{} +} + +func (a eventsource) Parse(ing *extensions.Ingress) (interface{}, error) { + return parser.GetBoolAnnotation(annotation, ing) +} diff --git a/core/pkg/ingress/annotations/eventsource/main_test.go b/core/pkg/ingress/annotations/eventsource/main_test.go new file mode 100644 index 000000000..bac1c03be --- /dev/null +++ b/core/pkg/ingress/annotations/eventsource/main_test.go @@ -0,0 +1,63 @@ +/* +Copyright 2017 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 eventsource + +import ( + "testing" + + api "k8s.io/api/core/v1" + extensions "k8s.io/api/extensions/v1beta1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + notEventsourceAnnotation = "ingress.kubernetes.io/not-eventsource" +) + +func TestParse(t *testing.T) { + ap := NewParser() + if ap == nil { + t.Fatalf("expected a parser.IngressAnnotation but returned nil") + } + + testCases := []struct { + annotations map[string]string + expected bool + }{ + {map[string]string{annotation: "true"}, true}, + {map[string]string{annotation: "false"}, false}, + {map[string]string{notEventsourceAnnotation: "true"}, false}, + {map[string]string{}, false}, + {nil, false}, + } + + ing := &extensions.Ingress{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "foo", + Namespace: api.NamespaceDefault, + }, + Spec: extensions.IngressSpec{}, + } + + for _, testCase := range testCases { + ing.SetAnnotations(testCase.annotations) + result, _ := ap.Parse(ing) + if result != testCase.expected { + t.Errorf("expected %t but returned %t, annotations: %s", testCase.expected, result, testCase.annotations) + } + } +} diff --git a/core/pkg/ingress/controller/annotations.go b/core/pkg/ingress/controller/annotations.go index 7aadb9ddd..3587ff88c 100644 --- a/core/pkg/ingress/controller/annotations.go +++ b/core/pkg/ingress/controller/annotations.go @@ -23,6 +23,7 @@ import ( "k8s.io/ingress/core/pkg/ingress/annotations/authreq" "k8s.io/ingress/core/pkg/ingress/annotations/authtls" "k8s.io/ingress/core/pkg/ingress/annotations/cors" + "k8s.io/ingress/core/pkg/ingress/annotations/eventsource" "k8s.io/ingress/core/pkg/ingress/annotations/healthcheck" "k8s.io/ingress/core/pkg/ingress/annotations/ipwhitelist" "k8s.io/ingress/core/pkg/ingress/annotations/parser" @@ -58,6 +59,7 @@ func newAnnotationExtractor(cfg extractorConfig) annotationExtractor { "ExternalAuth": authreq.NewParser(), "CertificateAuth": authtls.NewParser(cfg), "EnableCORS": cors.NewParser(), + "Eventsource": eventsource.NewParser(), "HealthCheck": healthcheck.NewParser(cfg), "Whitelist": ipwhitelist.NewParser(cfg), "UsePortInRedirects": portinredirect.NewParser(cfg), diff --git a/core/pkg/ingress/controller/util_test.go b/core/pkg/ingress/controller/util_test.go index 2aa6d5a20..8168d9304 100644 --- a/core/pkg/ingress/controller/util_test.go +++ b/core/pkg/ingress/controller/util_test.go @@ -97,6 +97,7 @@ func TestMergeLocationAnnotations(t *testing.T) { "BasicDigestAuth": auth.BasicDigest{}, DeniedKeyName: &fakeError{}, "EnableCORS": true, + "Eventsource": true, "ExternalAuth": authreq.External{}, "RateLimit": ratelimit.RateLimit{}, "Redirect": rewrite.Redirect{}, diff --git a/core/pkg/ingress/types.go b/core/pkg/ingress/types.go index 28a30aa07..dce7515c2 100644 --- a/core/pkg/ingress/types.go +++ b/core/pkg/ingress/types.go @@ -265,6 +265,9 @@ type Location struct { // EnableCORS indicates if path must support CORS // +optional EnableCORS bool `json:"enableCors,omitempty"` + // Eventsource indicates the path is a server sent events endpoint + // +optional + Eventsource bool `json:"eventsource,omitempty"` // ExternalAuth indicates the access to this location requires // authentication using an external provider // +optional diff --git a/core/pkg/ingress/types_equals.go b/core/pkg/ingress/types_equals.go index c0c63d923..37164e7e6 100644 --- a/core/pkg/ingress/types_equals.go +++ b/core/pkg/ingress/types_equals.go @@ -353,6 +353,9 @@ func (l1 *Location) Equal(l2 *Location) bool { if l1.EnableCORS != l2.EnableCORS { return false } + if l1.Eventsource != l2.Eventsource { + return false + } if !(&l1.ExternalAuth).Equal(&l2.ExternalAuth) { return false }