2 Copyright 2015 The Kubernetes Authors.
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
24 // MessageCountMap contains occurrence for each error message.
25 type MessageCountMap map[string]int
27 // Aggregate represents an object that contains multiple errors, but does not
28 // necessarily have singular semantic meaning.
29 type Aggregate interface {
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,
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 {
43 // In case of input error list contains nil
45 for _, e := range errlist {
47 errs = append(errs, e)
53 return aggregate(errs)
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
61 // Error is part of the error interface.
62 func (agg aggregate) Error() string {
64 // This should never happen, really.
70 result := fmt.Sprintf("[%s", agg[0].Error())
71 for i := 1; i < len(agg); i++ {
72 result += fmt.Sprintf(", %s", agg[i].Error())
78 // Errors is part of the Aggregate interface.
79 func (agg aggregate) Errors() []error {
83 // Matcher is used to match errors. Returns true if the error matches.
84 type Matcher func(error) bool
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.
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 {
97 if agg, ok := err.(Aggregate); ok {
98 return NewAggregate(filterErrors(agg.Errors(), fns...))
100 if !matchesError(err, fns...) {
106 // matchesError returns true if any Matcher returns true
107 func matchesError(err error, fns ...Matcher) bool {
108 for _, fn := range fns {
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 {
122 for _, err := range list {
123 r := FilterOut(err, fns...)
125 result = append(result, r)
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 {
138 for _, err := range agg.Errors() {
139 if a, ok := err.(Aggregate); ok {
142 result = append(result, r.Errors()...)
146 result = append(result, err)
150 return NewAggregate(result)
153 // CreateAggregateFromMessageCountMap converts MessageCountMap Aggregate
154 func CreateAggregateFromMessageCountMap(m MessageCountMap) Aggregate {
158 result := make([]error, 0, len(m))
159 for errStr, count := range m {
162 countStr = fmt.Sprintf(" (repeated %v times)", count)
164 result = append(result, fmt.Errorf("%v%v", errStr, countStr))
166 return NewAggregate(result)
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()) {
175 return agg.Errors()[0]
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)
191 errs := make([]error, 0)
192 for i := 0; i < cap(errChan); i++ {
193 if err := <-errChan; err != nil {
194 errs = append(errs, err)
197 return NewAggregate(errs)
200 // ErrPreconditionViolated is returned when the precondition is violated
201 var ErrPreconditionViolated = errors.New("precondition is violated")