From 382049a0bfc6db997a7f1c27dd79138ee3833d09 Mon Sep 17 00:00:00 2001 From: Anish Ramasekar Date: Fri, 12 Oct 2018 14:48:10 -0500 Subject: [PATCH] Adds support for HTTP2 Push Preload annotation update test for backendprotocols Adds support for HTTP2 Push Preload annotation Signed-off-by: Ricardo Pchevuzinske Katz Adds support for HTTP2 Push Preload annotation Signed-off-by: Ricardo Pchevuzinske Katz Adds support for HTTP2 Push Preload annotation Signed-off-by: Ricardo Pchevuzinske Katz Adds support for HTTP2 Push Preload annotation Signed-off-by: Ricardo Pchevuzinske Katz Adds support for HTTP2 Push Preload annotation Adds support for HTTP2 Push Preload annotation --- .../nginx-configuration/annotations.md | 9 +++ internal/ingress/annotations/annotations.go | 3 + .../annotations/http2pushpreload/main.go | 39 ++++++++++++ .../annotations/http2pushpreload/main_test.go | 62 +++++++++++++++++++ internal/ingress/controller/controller.go | 1 + internal/ingress/types.go | 4 ++ internal/ingress/types_equals.go | 3 + rootfs/etc/nginx/template/nginx.tmpl | 4 ++ test/e2e/annotations/http2pushpreload.go | 49 +++++++++++++++ 9 files changed, 174 insertions(+) create mode 100644 internal/ingress/annotations/http2pushpreload/main.go create mode 100644 internal/ingress/annotations/http2pushpreload/main_test.go create mode 100644 test/e2e/annotations/http2pushpreload.go diff --git a/docs/user-guide/nginx-configuration/annotations.md b/docs/user-guide/nginx-configuration/annotations.md index f9d3cd728..7854f2855 100644 --- a/docs/user-guide/nginx-configuration/annotations.md +++ b/docs/user-guide/nginx-configuration/annotations.md @@ -46,6 +46,7 @@ You can add these Kubernetes annotations to specific Ingress objects to customiz |[nginx.ingress.kubernetes.io/cors-max-age](#enable-cors)|number| |[nginx.ingress.kubernetes.io/force-ssl-redirect](#server-side-https-enforcement-through-redirect)|"true" or "false"| |[nginx.ingress.kubernetes.io/from-to-www-redirect](#redirect-from-to-www)|"true" or "false"| +|[nginx.ingress.kubernetes.io/http2-push-preload](#http2-push-preload)|"true" or "false"| |[nginx.ingress.kubernetes.io/limit-connections](#rate-limiting)|number| |[nginx.ingress.kubernetes.io/limit-rps](#rate-limiting)|number| |[nginx.ingress.kubernetes.io/permanent-redirect](#permanent-redirect)|string| @@ -298,6 +299,14 @@ CORS can be controlled with the following annotations: !!! note For more information please see [https://enable-cors.org](https://enable-cors.org/server_nginx.html) +### HTTP2 Push Preload. + +Enables automatic conversion of preload links specified in the “Link” response header fields into push requests. + +!!! example + + * `nginx.ingress.kubernetes.io/http2-push-preload: "true"` + ### Server Alias To add Server Aliases to an Ingress rule add the annotation `nginx.ingress.kubernetes.io/server-alias: ""`. diff --git a/internal/ingress/annotations/annotations.go b/internal/ingress/annotations/annotations.go index 5365fbe9a..0ada5d6a6 100644 --- a/internal/ingress/annotations/annotations.go +++ b/internal/ingress/annotations/annotations.go @@ -37,6 +37,7 @@ import ( "k8s.io/ingress-nginx/internal/ingress/annotations/cors" "k8s.io/ingress-nginx/internal/ingress/annotations/customhttperrors" "k8s.io/ingress-nginx/internal/ingress/annotations/defaultbackend" + "k8s.io/ingress-nginx/internal/ingress/annotations/http2pushpreload" "k8s.io/ingress-nginx/internal/ingress/annotations/influxdb" "k8s.io/ingress-nginx/internal/ingress/annotations/ipwhitelist" "k8s.io/ingress-nginx/internal/ingress/annotations/loadbalancing" @@ -80,6 +81,7 @@ type Ingress struct { DefaultBackend *apiv1.Service Denied error ExternalAuth authreq.Config + HTTP2PushPreload bool Proxy proxy.Config RateLimit ratelimit.Config Redirect redirect.Config @@ -122,6 +124,7 @@ func NewAnnotationExtractor(cfg resolver.Resolver) Extractor { "CustomHTTPErrors": customhttperrors.NewParser(cfg), "DefaultBackend": defaultbackend.NewParser(cfg), "ExternalAuth": authreq.NewParser(cfg), + "HTTP2PushPreload": http2pushpreload.NewParser(cfg), "Proxy": proxy.NewParser(cfg), "RateLimit": ratelimit.NewParser(cfg), "Redirect": redirect.NewParser(cfg), diff --git a/internal/ingress/annotations/http2pushpreload/main.go b/internal/ingress/annotations/http2pushpreload/main.go new file mode 100644 index 000000000..a86b3653f --- /dev/null +++ b/internal/ingress/annotations/http2pushpreload/main.go @@ -0,0 +1,39 @@ +/* +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 http2pushpreload + +import ( + extensions "k8s.io/api/extensions/v1beta1" + + "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/resolver" +) + +type http2PushPreload struct { + r resolver.Resolver +} + +// NewParser creates a new http2PushPreload annotation parser +func NewParser(r resolver.Resolver) parser.IngressAnnotation { + return http2PushPreload{r} +} + +// Parse parses the annotations contained in the ingress rule +// used to add http2 push preload to the server +func (h2pp http2PushPreload) Parse(ing *extensions.Ingress) (interface{}, error) { + return parser.GetBoolAnnotation("http2-push-preload", ing) +} diff --git a/internal/ingress/annotations/http2pushpreload/main_test.go b/internal/ingress/annotations/http2pushpreload/main_test.go new file mode 100644 index 000000000..c5ce586b2 --- /dev/null +++ b/internal/ingress/annotations/http2pushpreload/main_test.go @@ -0,0 +1,62 @@ +/* +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 http2pushpreload + +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" +) + +func TestParse(t *testing.T) { + annotation := parser.GetAnnotationWithPrefix("http2-push-preload") + ap := NewParser(&resolver.Mock{}) + 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: "1"}, true}, + {map[string]string{annotation: ""}, 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 %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) + } + } +} diff --git a/internal/ingress/controller/controller.go b/internal/ingress/controller/controller.go index d74b03b5a..ff8a3b7d7 100644 --- a/internal/ingress/controller/controller.go +++ b/internal/ingress/controller/controller.go @@ -473,6 +473,7 @@ func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*in loc.ConfigurationSnippet = anns.ConfigurationSnippet loc.CorsConfig = anns.CorsConfig loc.ExternalAuth = anns.ExternalAuth + loc.HTTP2PushPreload = anns.HTTP2PushPreload loc.Proxy = anns.Proxy loc.RateLimit = anns.RateLimit loc.Redirect = anns.Redirect diff --git a/internal/ingress/types.go b/internal/ingress/types.go index 05a5895cf..c70a5526f 100644 --- a/internal/ingress/types.go +++ b/internal/ingress/types.go @@ -238,6 +238,10 @@ type Location struct { // authentication using an external provider // +optional ExternalAuth authreq.Config `json:"externalAuth,omitempty"` + // HTTP2PushPreload allows to configure the HTTP2 Push Preload from backend + // original location. + // +optional + HTTP2PushPreload bool `json:"http2PushPreload,omitempty"` // RateLimit describes a limit in the number of connections per IP // address or connections per second. // The Redirect annotation precedes RateLimit diff --git a/internal/ingress/types_equals.go b/internal/ingress/types_equals.go index 6e65a5c72..eea824aad 100644 --- a/internal/ingress/types_equals.go +++ b/internal/ingress/types_equals.go @@ -374,6 +374,9 @@ func (l1 *Location) Equal(l2 *Location) bool { if !(&l1.ExternalAuth).Equal(&l2.ExternalAuth) { return false } + if l1.HTTP2PushPreload != l2.HTTP2PushPreload { + return false + } if !(&l1.RateLimit).Equal(&l2.RateLimit) { return false } diff --git a/rootfs/etc/nginx/template/nginx.tmpl b/rootfs/etc/nginx/template/nginx.tmpl index 877186bce..c8473afa7 100644 --- a/rootfs/etc/nginx/template/nginx.tmpl +++ b/rootfs/etc/nginx/template/nginx.tmpl @@ -1122,6 +1122,10 @@ stream { rewrite_log on; {{ end }} + {{ if $location.HTTP2PushPreload }} + http2_push_preload on; + {{ end }} + port_in_redirect {{ if $location.UsePortInRedirects }}on{{ else }}off{{ end }}; set $proxy_upstream_name "{{ buildUpstreamName $location }}"; diff --git a/test/e2e/annotations/http2pushpreload.go b/test/e2e/annotations/http2pushpreload.go new file mode 100644 index 000000000..66211e77f --- /dev/null +++ b/test/e2e/annotations/http2pushpreload.go @@ -0,0 +1,49 @@ +/* +Copyright 2018 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 annotations + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/ingress-nginx/test/e2e/framework" +) + +var _ = framework.IngressNginxDescribe("Annotations - HTTP2 Push Preload", func() { + f := framework.NewDefaultFramework("http2pushpreload") + + BeforeEach(func() { + f.NewEchoDeploymentWithReplicas(2) + }) + + AfterEach(func() { + }) + + It("enable the http2-push-preload directive", func() { + host := "http2pp.foo.com" + annotations := map[string]string{ + "nginx.ingress.kubernetes.io/http2-push-preload": "true", + } + + ing := framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, &annotations) + f.EnsureIngress(ing) + + f.WaitForNginxServer(host, + func(server string) bool { + return Expect(server).Should(ContainSubstring("http2_push_preload on;")) + }) + }) +})