mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 05:27:21 +00:00
Merge pull request #68001 from sttts/sttts-timeout-panic-forward
Automatic merge from submit-queue (batch tested with PRs 66577, 67948, 68001, 67982). If you want to cherry-pick this change to another branch, please follow the instructions here: https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md. apiserver: forward panic in WithTimeout filter ```release-note Return apiserver panics as 500 errors instead terminating the apiserver process. ``` Without this PR a panic in a HTTP handler will not be caught in the Go routine started by the timeout filter. Uncaught panics terminate the process. This is a strong condidate to be backported to 1.11, 1.10 and 1.9.
This commit is contained in:
commit
ca8f267cc8
@ -20,6 +20,7 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/filters:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/endpoints/filters:go_default_library",
|
||||||
|
@ -91,14 +91,19 @@ func (t *timeoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
done := make(chan struct{})
|
result := make(chan interface{})
|
||||||
tw := newTimeoutWriter(w)
|
tw := newTimeoutWriter(w)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
result <- recover()
|
||||||
|
}()
|
||||||
t.handler.ServeHTTP(tw, r)
|
t.handler.ServeHTTP(tw, r)
|
||||||
close(done)
|
|
||||||
}()
|
}()
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case err := <-result:
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
case <-after:
|
case <-after:
|
||||||
postTimeoutFn()
|
postTimeoutFn()
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/diff"
|
"k8s.io/apimachinery/pkg/util/diff"
|
||||||
|
"k8s.io/apimachinery/pkg/util/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
type recorder struct {
|
type recorder struct {
|
||||||
@ -50,22 +51,33 @@ func (r *recorder) Count() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTimeout(t *testing.T) {
|
func TestTimeout(t *testing.T) {
|
||||||
|
origReallyCrash := runtime.ReallyCrash
|
||||||
|
runtime.ReallyCrash = false
|
||||||
|
defer func() {
|
||||||
|
runtime.ReallyCrash = origReallyCrash
|
||||||
|
}()
|
||||||
|
|
||||||
sendResponse := make(chan struct{}, 1)
|
sendResponse := make(chan struct{}, 1)
|
||||||
|
doPanic := make(chan struct{}, 1)
|
||||||
writeErrors := make(chan error, 1)
|
writeErrors := make(chan error, 1)
|
||||||
timeout := make(chan time.Time, 1)
|
timeout := make(chan time.Time, 1)
|
||||||
resp := "test response"
|
resp := "test response"
|
||||||
timeoutErr := apierrors.NewServerTimeout(schema.GroupResource{Group: "foo", Resource: "bar"}, "get", 0)
|
timeoutErr := apierrors.NewServerTimeout(schema.GroupResource{Group: "foo", Resource: "bar"}, "get", 0)
|
||||||
record := &recorder{}
|
record := &recorder{}
|
||||||
|
|
||||||
ts := httptest.NewServer(WithTimeout(http.HandlerFunc(
|
ts := httptest.NewServer(WithPanicRecovery(WithTimeout(http.HandlerFunc(
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
<-sendResponse
|
select {
|
||||||
|
case <-sendResponse:
|
||||||
_, err := w.Write([]byte(resp))
|
_, err := w.Write([]byte(resp))
|
||||||
writeErrors <- err
|
writeErrors <- err
|
||||||
|
case <-doPanic:
|
||||||
|
panic("inner handler panics")
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
func(req *http.Request) (*http.Request, <-chan time.Time, func(), *apierrors.StatusError) {
|
func(req *http.Request) (*http.Request, <-chan time.Time, func(), *apierrors.StatusError) {
|
||||||
return req, timeout, record.Record, timeoutErr
|
return req, timeout, record.Record, timeoutErr
|
||||||
}))
|
})))
|
||||||
defer ts.Close()
|
defer ts.Close()
|
||||||
|
|
||||||
// No timeouts
|
// No timeouts
|
||||||
@ -114,4 +126,14 @@ func TestTimeout(t *testing.T) {
|
|||||||
if err := <-writeErrors; err != http.ErrHandlerTimeout {
|
if err := <-writeErrors; err != http.ErrHandlerTimeout {
|
||||||
t.Errorf("got Write error of %v; expected %v", err, http.ErrHandlerTimeout)
|
t.Errorf("got Write error of %v; expected %v", err, http.ErrHandlerTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Panics
|
||||||
|
doPanic <- struct{}{}
|
||||||
|
res, err = http.Get(ts.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusInternalServerError {
|
||||||
|
t.Errorf("got res.StatusCode %d; expected %d due to panic", res.StatusCode, http.StatusInternalServerError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user