1 /* 2 Copyright 2023 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 wait 18 19 import ( 20 "context" 21 "errors" 22 ) 23 24 // ErrWaitTimeout is returned when the condition was not satisfied in time. 25 // 26 // Deprecated: This type will be made private in favor of Interrupted() 27 // for checking errors or ErrorInterrupted(err) for returning a wrapped error. 28 var ErrWaitTimeout = ErrorInterrupted(errors.New("timed out waiting for the condition")) 29 30 // Interrupted returns true if the error indicates a Poll, ExponentialBackoff, or 31 // Until loop exited for any reason besides the condition returning true or an 32 // error. A loop is considered interrupted if the calling context is cancelled, 33 // the context reaches its deadline, or a backoff reaches its maximum allowed 34 // steps. 35 // 36 // Callers should use this method instead of comparing the error value directly to 37 // ErrWaitTimeout, as methods that cancel a context may not return that error. 38 // 39 // Instead of: 40 // 41 // err := wait.Poll(...) 42 // if err == wait.ErrWaitTimeout { 43 // log.Infof("Wait for operation exceeded") 44 // } else ... 45 // 46 // Use: 47 // 48 // err := wait.Poll(...) 49 // if wait.Interrupted(err) { 50 // log.Infof("Wait for operation exceeded") 51 // } else ... 52 func Interrupted(err error) bool { 53 switch { 54 case errors.Is(err, errWaitTimeout), 55 errors.Is(err, context.Canceled), 56 errors.Is(err, context.DeadlineExceeded): 57 return true 58 default: 59 return false 60 } 61 } 62 63 // errInterrupted 64 type errInterrupted struct { 65 cause error 66 } 67 68 // ErrorInterrupted returns an error that indicates the wait was ended 69 // early for a given reason. If no cause is provided a generic error 70 // will be used but callers are encouraged to provide a real cause for 71 // clarity in debugging. 72 func ErrorInterrupted(cause error) error { 73 switch cause.(type) { 74 case errInterrupted: 75 // no need to wrap twice since errInterrupted is only needed 76 // once in a chain 77 return cause 78 default: 79 return errInterrupted{cause} 80 } 81 } 82 83 // errWaitTimeout is the private version of the previous ErrWaitTimeout 84 // and is private to prevent direct comparison. Use ErrorInterrupted(err) 85 // to get an error that will return true for Interrupted(err). 86 var errWaitTimeout = errInterrupted{} 87 88 func (e errInterrupted) Unwrap() error { return e.cause } 89 func (e errInterrupted) Is(target error) bool { return target == errWaitTimeout } 90 func (e errInterrupted) Error() string { 91 if e.cause == nil { 92 // returns the same error message as historical behavior 93 return "timed out waiting for the condition" 94 } 95 return e.cause.Error() 96 } 97