k8s.io/apimachinery@v0.29.2/pkg/util/runtime/runtime_test.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package runtime 18 19 import ( 20 "bytes" 21 "fmt" 22 "io" 23 "net/http" 24 "os" 25 "regexp" 26 "strings" 27 "sync" 28 "testing" 29 "time" 30 ) 31 32 func TestHandleCrash(t *testing.T) { 33 defer func() { 34 if x := recover(); x == nil { 35 t.Errorf("Expected a panic to recover from") 36 } 37 }() 38 defer HandleCrash() 39 panic("Test Panic") 40 } 41 42 func TestCustomHandleCrash(t *testing.T) { 43 old := PanicHandlers 44 defer func() { PanicHandlers = old }() 45 var result interface{} 46 PanicHandlers = []func(interface{}){ 47 func(r interface{}) { 48 result = r 49 }, 50 } 51 func() { 52 defer func() { 53 if x := recover(); x == nil { 54 t.Errorf("Expected a panic to recover from") 55 } 56 }() 57 defer HandleCrash() 58 panic("test") 59 }() 60 if result != "test" { 61 t.Errorf("did not receive custom handler") 62 } 63 } 64 65 func TestCustomHandleError(t *testing.T) { 66 old := ErrorHandlers 67 defer func() { ErrorHandlers = old }() 68 var result error 69 ErrorHandlers = []func(error){ 70 func(err error) { 71 result = err 72 }, 73 } 74 err := fmt.Errorf("test") 75 HandleError(err) 76 if result != err { 77 t.Errorf("did not receive custom handler") 78 } 79 } 80 81 func TestHandleCrashLog(t *testing.T) { 82 log, err := captureStderr(func() { 83 defer func() { 84 if r := recover(); r == nil { 85 t.Fatalf("expected a panic to recover from") 86 } 87 }() 88 defer HandleCrash() 89 panic("test panic") 90 }) 91 if err != nil { 92 t.Fatalf("%v", err) 93 } 94 // Example log: 95 // 96 // ...] Observed a panic: test panic 97 // goroutine 6 [running]: 98 // command-line-arguments.logPanic(0x..., 0x...) 99 // .../src/k8s.io/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/runtime/runtime.go:69 +0x... 100 lines := strings.Split(log, "\n") 101 if len(lines) < 4 { 102 t.Fatalf("panic log should have 1 line of message, 1 line per goroutine and 2 lines per function call") 103 } 104 if match, _ := regexp.MatchString("Observed a panic: test panic", lines[0]); !match { 105 t.Errorf("mismatch panic message: %s", lines[0]) 106 } 107 // The following regexp's verify that Kubernetes panic log matches Golang stdlib 108 // stacktrace pattern. We need to update these regexp's if stdlib changes its pattern. 109 if match, _ := regexp.MatchString(`goroutine [0-9]+ \[.+\]:`, lines[1]); !match { 110 t.Errorf("mismatch goroutine: %s", lines[1]) 111 } 112 if match, _ := regexp.MatchString(`logPanic(.*)`, lines[2]); !match { 113 t.Errorf("mismatch symbolized function name: %s", lines[2]) 114 } 115 if match, _ := regexp.MatchString(`runtime\.go:[0-9]+ \+0x`, lines[3]); !match { 116 t.Errorf("mismatch file/line/offset information: %s", lines[3]) 117 } 118 } 119 120 func TestHandleCrashLogSilenceHTTPErrAbortHandler(t *testing.T) { 121 log, err := captureStderr(func() { 122 defer func() { 123 if r := recover(); r != http.ErrAbortHandler { 124 t.Fatalf("expected to recover from http.ErrAbortHandler") 125 } 126 }() 127 defer HandleCrash() 128 panic(http.ErrAbortHandler) 129 }) 130 if err != nil { 131 t.Fatalf("%v", err) 132 } 133 if len(log) > 0 { 134 t.Fatalf("expected no stderr log, got: %s", log) 135 } 136 } 137 138 // captureStderr redirects stderr to result string, and then restore stderr from backup 139 func captureStderr(f func()) (string, error) { 140 r, w, err := os.Pipe() 141 if err != nil { 142 return "", err 143 } 144 bak := os.Stderr 145 os.Stderr = w 146 defer func() { os.Stderr = bak }() 147 148 resultCh := make(chan string) 149 // copy the output in a separate goroutine so printing can't block indefinitely 150 go func() { 151 var buf bytes.Buffer 152 io.Copy(&buf, r) 153 resultCh <- buf.String() 154 }() 155 156 f() 157 w.Close() 158 159 return <-resultCh, nil 160 } 161 162 func Test_rudimentaryErrorBackoff_OnError_ParallelSleep(t *testing.T) { 163 r := &rudimentaryErrorBackoff{ 164 minPeriod: time.Second, 165 } 166 167 start := make(chan struct{}) 168 var wg sync.WaitGroup 169 for i := 0; i < 30; i++ { 170 wg.Add(1) 171 go func() { 172 <-start 173 r.OnError(nil) // input error is ignored 174 wg.Done() 175 }() 176 } 177 st := time.Now() 178 close(start) 179 wg.Wait() 180 181 if since := time.Since(st); since > 5*time.Second { 182 t.Errorf("OnError slept for too long: %s", since) 183 } 184 }