From 93a46dca8a3dfdef7ce1f7d5d0e577beac056fc4 Mon Sep 17 00:00:00 2001 From: Abu Kashem Date: Wed, 3 Feb 2021 14:05:59 -0500 Subject: [PATCH] UPSTREAM: 96901: apiserver: add e2e tests for request context deadline --- test/e2e/apimachinery/BUILD | 1 + test/e2e/apimachinery/request_timeout.go | 107 +++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 test/e2e/apimachinery/request_timeout.go diff --git a/test/e2e/apimachinery/BUILD b/test/e2e/apimachinery/BUILD index 94ed3b962d489..39f4263edd90f 100644 --- a/test/e2e/apimachinery/BUILD +++ b/test/e2e/apimachinery/BUILD @@ -25,6 +25,7 @@ go_library( "health_handlers.go", "namespace.go", "protocol.go", + "request_timeout.go", "resource_quota.go", "server_version.go", "table_conversion.go", diff --git a/test/e2e/apimachinery/request_timeout.go b/test/e2e/apimachinery/request_timeout.go new file mode 100644 index 0000000000000..c6e1b41383501 --- /dev/null +++ b/test/e2e/apimachinery/request_timeout.go @@ -0,0 +1,107 @@ +/* +Copyright 2020 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 apimachinery + +import ( + "io/ioutil" + "net/http" + "strings" + + "github.com/onsi/ginkgo" + "k8s.io/client-go/rest" + "k8s.io/kubernetes/test/e2e/framework" +) + +const ( + invalidTimeoutMessageExpected = "invalid timeout specified in the request URL" +) + +var _ = SIGDescribe("Server request timeout", func() { + f := framework.NewDefaultFramework("request-timeout") + + ginkgo.It("should return HTTP status code 400 if the user specifies an invalid timeout in the request URL", func() { + rt := getRoundTripper(f) + req := newRequest(f, "invalid") + + response, err := rt.RoundTrip(req) + framework.ExpectNoError(err) + defer response.Body.Close() + + if response.StatusCode != http.StatusBadRequest { + framework.Failf("expected HTTP status code: %d, but got: %d", http.StatusBadRequest, response.StatusCode) + } + + messageGot := readBody(response) + if !strings.Contains(messageGot, invalidTimeoutMessageExpected) { + framework.Failf("expected HTTP status message to contain: %s, but got: %s", invalidTimeoutMessageExpected, messageGot) + } + }) + + ginkgo.It("the request should be served with a default timeout if the specified timeout in the request URL exceeds maximum allowed", func() { + rt := getRoundTripper(f) + // Choose a timeout that exceeds the default timeout (60s) enforced by the apiserver + req := newRequest(f, "3m") + + response, err := rt.RoundTrip(req) + framework.ExpectNoError(err) + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + framework.Failf("expected HTTP status code: %d, but got: %d", http.StatusOK, response.StatusCode) + } + }) + + ginkgo.It("default timeout should be used if the specified timeout in the request URL is 0s", func() { + rt := getRoundTripper(f) + req := newRequest(f, "0s") + + response, err := rt.RoundTrip(req) + framework.ExpectNoError(err) + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + framework.Failf("expected HTTP status code: %d, but got: %d", http.StatusOK, response.StatusCode) + } + }) +}) + +func getRoundTripper(f *framework.Framework) http.RoundTripper { + config := rest.CopyConfig(f.ClientConfig()) + + // ensure we don't enforce any transport timeout from the client side. + config.Timeout = 0 + + roundTripper, err := rest.TransportFor(config) + framework.ExpectNoError(err) + + return roundTripper +} + +func newRequest(f *framework.Framework, timeout string) *http.Request { + req, err := http.NewRequest(http.MethodGet, f.ClientSet.CoreV1().RESTClient().Get(). + Param("timeout", timeout).AbsPath("version").URL().String(), nil) + framework.ExpectNoError(err) + + return req +} + +func readBody(response *http.Response) string { + raw, err := ioutil.ReadAll(response.Body) + framework.ExpectNoError(err) + + return string(raw) +}