Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / k8s.io / apimachinery / pkg / util / runtime / runtime.go
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         "fmt"
21         "runtime"
22         "sync"
23         "time"
24
25         "k8s.io/klog"
26 )
27
28 var (
29         // ReallyCrash controls the behavior of HandleCrash and now defaults
30         // true. It's still exposed so components can optionally set to false
31         // to restore prior behavior.
32         ReallyCrash = true
33 )
34
35 // PanicHandlers is a list of functions which will be invoked when a panic happens.
36 var PanicHandlers = []func(interface{}){logPanic}
37
38 // HandleCrash simply catches a crash and logs an error. Meant to be called via
39 // defer.  Additional context-specific handlers can be provided, and will be
40 // called in case of panic.  HandleCrash actually crashes, after calling the
41 // handlers and logging the panic message.
42 //
43 // TODO: remove this function. We are switching to a world where it's safe for
44 // apiserver to panic, since it will be restarted by kubelet. At the beginning
45 // of the Kubernetes project, nothing was going to restart apiserver and so
46 // catching panics was important. But it's actually much simpler for monitoring
47 // software if we just exit when an unexpected panic happens.
48 func HandleCrash(additionalHandlers ...func(interface{})) {
49         if r := recover(); r != nil {
50                 for _, fn := range PanicHandlers {
51                         fn(r)
52                 }
53                 for _, fn := range additionalHandlers {
54                         fn(r)
55                 }
56                 if ReallyCrash {
57                         // Actually proceed to panic.
58                         panic(r)
59                 }
60         }
61 }
62
63 // logPanic logs the caller tree when a panic occurs.
64 func logPanic(r interface{}) {
65         callers := getCallers(r)
66         if _, ok := r.(string); ok {
67                 klog.Errorf("Observed a panic: %s\n%v", r, callers)
68         } else {
69                 klog.Errorf("Observed a panic: %#v (%v)\n%v", r, r, callers)
70         }
71 }
72
73 func getCallers(r interface{}) string {
74         callers := ""
75         for i := 0; true; i++ {
76                 _, file, line, ok := runtime.Caller(i)
77                 if !ok {
78                         break
79                 }
80                 callers = callers + fmt.Sprintf("%v:%v\n", file, line)
81         }
82
83         return callers
84 }
85
86 // ErrorHandlers is a list of functions which will be invoked when an unreturnable
87 // error occurs.
88 // TODO(lavalamp): for testability, this and the below HandleError function
89 // should be packaged up into a testable and reusable object.
90 var ErrorHandlers = []func(error){
91         logError,
92         (&rudimentaryErrorBackoff{
93                 lastErrorTime: time.Now(),
94                 // 1ms was the number folks were able to stomach as a global rate limit.
95                 // If you need to log errors more than 1000 times a second you
96                 // should probably consider fixing your code instead. :)
97                 minPeriod: time.Millisecond,
98         }).OnError,
99 }
100
101 // HandlerError is a method to invoke when a non-user facing piece of code cannot
102 // return an error and needs to indicate it has been ignored. Invoking this method
103 // is preferable to logging the error - the default behavior is to log but the
104 // errors may be sent to a remote server for analysis.
105 func HandleError(err error) {
106         // this is sometimes called with a nil error.  We probably shouldn't fail and should do nothing instead
107         if err == nil {
108                 return
109         }
110
111         for _, fn := range ErrorHandlers {
112                 fn(err)
113         }
114 }
115
116 // logError prints an error with the call stack of the location it was reported
117 func logError(err error) {
118         klog.ErrorDepth(2, err)
119 }
120
121 type rudimentaryErrorBackoff struct {
122         minPeriod time.Duration // immutable
123         // TODO(lavalamp): use the clock for testability. Need to move that
124         // package for that to be accessible here.
125         lastErrorTimeLock sync.Mutex
126         lastErrorTime     time.Time
127 }
128
129 // OnError will block if it is called more often than the embedded period time.
130 // This will prevent overly tight hot error loops.
131 func (r *rudimentaryErrorBackoff) OnError(error) {
132         r.lastErrorTimeLock.Lock()
133         defer r.lastErrorTimeLock.Unlock()
134         d := time.Since(r.lastErrorTime)
135         if d < r.minPeriod {
136                 // If the time moves backwards for any reason, do nothing
137                 time.Sleep(r.minPeriod - d)
138         }
139         r.lastErrorTime = time.Now()
140 }
141
142 // GetCaller returns the caller of the function that calls it.
143 func GetCaller() string {
144         var pc [1]uintptr
145         runtime.Callers(3, pc[:])
146         f := runtime.FuncForPC(pc[0])
147         if f == nil {
148                 return fmt.Sprintf("Unable to find caller")
149         }
150         return f.Name()
151 }
152
153 // RecoverFromPanic replaces the specified error with an error containing the
154 // original error, and  the call tree when a panic occurs. This enables error
155 // handlers to handle errors and panics the same way.
156 func RecoverFromPanic(err *error) {
157         if r := recover(); r != nil {
158                 callers := getCallers(r)
159
160                 *err = fmt.Errorf(
161                         "recovered from panic %q. (err=%v) Call stack:\n%v",
162                         r,
163                         *err,
164                         callers)
165         }
166 }
167
168 // Must panics on non-nil errors.  Useful to handling programmer level errors.
169 func Must(err error) {
170         if err != nil {
171                 panic(err)
172         }
173 }