Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / k8s.io / apimachinery / pkg / util / errors / errors.go
1 /*
2 Copyright 2015 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 errors
18
19 import (
20         "errors"
21         "fmt"
22 )
23
24 // MessageCountMap contains occurrence for each error message.
25 type MessageCountMap map[string]int
26
27 // Aggregate represents an object that contains multiple errors, but does not
28 // necessarily have singular semantic meaning.
29 type Aggregate interface {
30         error
31         Errors() []error
32 }
33
34 // NewAggregate converts a slice of errors into an Aggregate interface, which
35 // is itself an implementation of the error interface.  If the slice is empty,
36 // this returns nil.
37 // It will check if any of the element of input error list is nil, to avoid
38 // nil pointer panic when call Error().
39 func NewAggregate(errlist []error) Aggregate {
40         if len(errlist) == 0 {
41                 return nil
42         }
43         // In case of input error list contains nil
44         var errs []error
45         for _, e := range errlist {
46                 if e != nil {
47                         errs = append(errs, e)
48                 }
49         }
50         if len(errs) == 0 {
51                 return nil
52         }
53         return aggregate(errs)
54 }
55
56 // This helper implements the error and Errors interfaces.  Keeping it private
57 // prevents people from making an aggregate of 0 errors, which is not
58 // an error, but does satisfy the error interface.
59 type aggregate []error
60
61 // Error is part of the error interface.
62 func (agg aggregate) Error() string {
63         if len(agg) == 0 {
64                 // This should never happen, really.
65                 return ""
66         }
67         if len(agg) == 1 {
68                 return agg[0].Error()
69         }
70         result := fmt.Sprintf("[%s", agg[0].Error())
71         for i := 1; i < len(agg); i++ {
72                 result += fmt.Sprintf(", %s", agg[i].Error())
73         }
74         result += "]"
75         return result
76 }
77
78 // Errors is part of the Aggregate interface.
79 func (agg aggregate) Errors() []error {
80         return []error(agg)
81 }
82
83 // Matcher is used to match errors.  Returns true if the error matches.
84 type Matcher func(error) bool
85
86 // FilterOut removes all errors that match any of the matchers from the input
87 // error.  If the input is a singular error, only that error is tested.  If the
88 // input implements the Aggregate interface, the list of errors will be
89 // processed recursively.
90 //
91 // This can be used, for example, to remove known-OK errors (such as io.EOF or
92 // os.PathNotFound) from a list of errors.
93 func FilterOut(err error, fns ...Matcher) error {
94         if err == nil {
95                 return nil
96         }
97         if agg, ok := err.(Aggregate); ok {
98                 return NewAggregate(filterErrors(agg.Errors(), fns...))
99         }
100         if !matchesError(err, fns...) {
101                 return err
102         }
103         return nil
104 }
105
106 // matchesError returns true if any Matcher returns true
107 func matchesError(err error, fns ...Matcher) bool {
108         for _, fn := range fns {
109                 if fn(err) {
110                         return true
111                 }
112         }
113         return false
114 }
115
116 // filterErrors returns any errors (or nested errors, if the list contains
117 // nested Errors) for which all fns return false. If no errors
118 // remain a nil list is returned. The resulting silec will have all
119 // nested slices flattened as a side effect.
120 func filterErrors(list []error, fns ...Matcher) []error {
121         result := []error{}
122         for _, err := range list {
123                 r := FilterOut(err, fns...)
124                 if r != nil {
125                         result = append(result, r)
126                 }
127         }
128         return result
129 }
130
131 // Flatten takes an Aggregate, which may hold other Aggregates in arbitrary
132 // nesting, and flattens them all into a single Aggregate, recursively.
133 func Flatten(agg Aggregate) Aggregate {
134         result := []error{}
135         if agg == nil {
136                 return nil
137         }
138         for _, err := range agg.Errors() {
139                 if a, ok := err.(Aggregate); ok {
140                         r := Flatten(a)
141                         if r != nil {
142                                 result = append(result, r.Errors()...)
143                         }
144                 } else {
145                         if err != nil {
146                                 result = append(result, err)
147                         }
148                 }
149         }
150         return NewAggregate(result)
151 }
152
153 // CreateAggregateFromMessageCountMap converts MessageCountMap Aggregate
154 func CreateAggregateFromMessageCountMap(m MessageCountMap) Aggregate {
155         if m == nil {
156                 return nil
157         }
158         result := make([]error, 0, len(m))
159         for errStr, count := range m {
160                 var countStr string
161                 if count > 1 {
162                         countStr = fmt.Sprintf(" (repeated %v times)", count)
163                 }
164                 result = append(result, fmt.Errorf("%v%v", errStr, countStr))
165         }
166         return NewAggregate(result)
167 }
168
169 // Reduce will return err or, if err is an Aggregate and only has one item,
170 // the first item in the aggregate.
171 func Reduce(err error) error {
172         if agg, ok := err.(Aggregate); ok && err != nil {
173                 switch len(agg.Errors()) {
174                 case 1:
175                         return agg.Errors()[0]
176                 case 0:
177                         return nil
178                 }
179         }
180         return err
181 }
182
183 // AggregateGoroutines runs the provided functions in parallel, stuffing all
184 // non-nil errors into the returned Aggregate.
185 // Returns nil if all the functions complete successfully.
186 func AggregateGoroutines(funcs ...func() error) Aggregate {
187         errChan := make(chan error, len(funcs))
188         for _, f := range funcs {
189                 go func(f func() error) { errChan <- f() }(f)
190         }
191         errs := make([]error, 0)
192         for i := 0; i < cap(errChan); i++ {
193                 if err := <-errChan; err != nil {
194                         errs = append(errs, err)
195                 }
196         }
197         return NewAggregate(errs)
198 }
199
200 // ErrPreconditionViolated is returned when the precondition is violated
201 var ErrPreconditionViolated = errors.New("precondition is violated")