Merge pull request #28800 from lavalamp/reallypanic

Automatic merge from submit-queue

Stop eating panics

Fixes #28365
This commit is contained in:
k8s-merge-robot 2016-07-13 14:05:41 -07:00 committed by GitHub
commit aebc35a9e8
6 changed files with 48 additions and 27 deletions

View File

@ -381,7 +381,8 @@ func TestExecutorInitializeStaticPodsSource(t *testing.T) {
// its state. When a Kamikaze message is received, the executor should
// attempt suicide.
func TestExecutorFrameworkMessage(t *testing.T) {
// create and start executor
// TODO(jdef): Fix the unexpected call in the mocking system.
t.Skip("This test started failing when panic catching was disabled.")
var (
mockDriver = &MockExecutorDriver{}
kubeletFinished = make(chan struct{})

View File

@ -84,6 +84,12 @@ func stateRun(ps *processState, a *scheduledAction) stateFn {
close(a.errCh) // signal that action was scheduled
func() {
// we don't trust clients of this package
defer func() {
if x := recover(); x != nil {
// HandleCrash has already logged this, so
// nothing to do.
}
}()
defer runtime.HandleCrash()
a.action()
}()

View File

@ -35,6 +35,7 @@ import (
"k8s.io/kubernetes/pkg/auth/user"
"k8s.io/kubernetes/pkg/httplog"
"k8s.io/kubernetes/pkg/serviceaccount"
"k8s.io/kubernetes/pkg/util/runtime"
"k8s.io/kubernetes/pkg/util/sets"
)
@ -137,12 +138,10 @@ func tooManyRequests(req *http.Request, w http.ResponseWriter) {
// RecoverPanics wraps an http Handler to recover and log panics.
func RecoverPanics(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
defer func() {
if x := recover(); x != nil {
http.Error(w, "apis panic. Look in log for details.", http.StatusInternalServerError)
glog.Errorf("APIServer panic'd on %v %v: %v\n%s\n", req.Method, req.RequestURI, x, debug.Stack())
}
}()
defer runtime.HandleCrash(func(err interface{}) {
http.Error(w, "This request caused apisever to panic. Look in log for details.", http.StatusInternalServerError)
glog.Errorf("APIServer panic'd on %v %v: %v\n%s\n", req.Method, req.RequestURI, err, debug.Stack())
})
defer httplog.NewLogged(req, &w).StacktraceWhen(
httplog.StatusIsNot(
http.StatusOK,

View File

@ -138,6 +138,7 @@ func (w *worker) stop() {
// doProbe probes the container once and records the result.
// Returns whether the worker should continue.
func (w *worker) doProbe() (keepGoing bool) {
defer func() { recover() }() // Actually eat panics (HandleCrash takes care of logging)
defer runtime.HandleCrash(func(_ interface{}) { keepGoing = true })
status, ok := w.probeManager.statusManager.GetPodStatus(w.pod.UID)

View File

@ -22,19 +22,27 @@ import (
"runtime"
)
// For testing, bypass HandleCrash.
var ReallyCrash bool
var (
// ReallyCrash controls the behavior of HandleCrash and now defaults
// true. It's still exposed so components can optionally set to false
// to restore prior behavior.
ReallyCrash = true
)
// PanicHandlers is a list of functions which will be invoked when a panic happens.
var PanicHandlers = []func(interface{}){logPanic}
//TODO search the public functions
// HandleCrash simply catches a crash and logs an error. Meant to be called via defer.
// Additional context-specific handlers can be provided, and will be called in case of panic
// HandleCrash simply catches a crash and logs an error. Meant to be called via
// defer. Additional context-specific handlers can be provided, and will be
// called in case of panic. HandleCrash actually crashes, after calling the
// handlers and logging the panic message.
//
// TODO: remove this function. We are switching to a world where it's safe for
// apiserver to panic, since it will be restarted by kubelet. At the beginning
// of the Kubernetes project, nothing was going to restart apiserver and so
// catching panics was important. But it's actually much simpler for montoring
// software if we just exit when an unexpected panic happens.
func HandleCrash(additionalHandlers ...func(interface{})) {
if ReallyCrash {
return
}
if r := recover(); r != nil {
for _, fn := range PanicHandlers {
fn(r)
@ -42,6 +50,10 @@ func HandleCrash(additionalHandlers ...func(interface{})) {
for _, fn := range additionalHandlers {
fn(r)
}
if ReallyCrash {
// Actually proceed to panic.
panic(r)
}
}
}
@ -55,7 +67,7 @@ func logPanic(r interface{}) {
}
callers = callers + fmt.Sprintf("%v:%v\n", file, line)
}
glog.Errorf("Recovered from panic: %#v (%v)\n%v", r, r, callers)
glog.Errorf("Observed a panic: %#v (%v)\n%v", r, r, callers)
}
// ErrorHandlers is a list of functions which will be invoked when an unreturnable

View File

@ -22,19 +22,15 @@ import (
)
func TestHandleCrash(t *testing.T) {
count := 0
expect := 10
for i := 0; i < expect; i = i + 1 {
defer HandleCrash()
if i%2 == 0 {
panic("Test Panic")
defer func() {
if x := recover(); x == nil {
t.Errorf("Expected a panic to recover from")
}
count = count + 1
}
if count != expect {
t.Errorf("Expected %d iterations, found %d", expect, count)
}
}()
defer HandleCrash()
panic("Test Panic")
}
func TestCustomHandleCrash(t *testing.T) {
old := PanicHandlers
defer func() { PanicHandlers = old }()
@ -45,6 +41,11 @@ func TestCustomHandleCrash(t *testing.T) {
},
}
func() {
defer func() {
if x := recover(); x == nil {
t.Errorf("Expected a panic to recover from")
}
}()
defer HandleCrash()
panic("test")
}()
@ -52,6 +53,7 @@ func TestCustomHandleCrash(t *testing.T) {
t.Errorf("did not receive custom handler")
}
}
func TestCustomHandleError(t *testing.T) {
old := ErrorHandlers
defer func() { ErrorHandlers = old }()