It's common in many concurrent engines to have a situation where we collect errors on shutdown. Errors can either because a context closed, or because some engine error happened. The latter, can also cause the former, leading to a list of returned errors. In these scenarios, we want to filter out all the secondary context errors, unless that's all that's there. This provides a helper function to do so.
121 lines
3.7 KiB
Go
121 lines
3.7 KiB
Go
// Mgmt
|
|
// Copyright (C) James Shubin and the project contributors
|
|
// Written by James Shubin <james@shubin.ca> and the project contributors
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
//
|
|
// Additional permission under GNU GPL version 3 section 7
|
|
//
|
|
// If you modify this program, or any covered work, by linking or combining it
|
|
// with embedded mcl code and modules (and that the embedded mcl code and
|
|
// modules which link with this program, contain a copy of their source code in
|
|
// the authoritative form) containing parts covered by the terms of any other
|
|
// license, the licensors of this program grant you additional permission to
|
|
// convey the resulting work. Furthermore, the licensors of this program grant
|
|
// the original author, James Shubin, additional permission to update this
|
|
// additional permission if he deems it necessary to achieve the goals of this
|
|
// additional permission.
|
|
|
|
// Package errwrap contains some error helpers.
|
|
package errwrap
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/hashicorp/go-multierror"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// Wrapf adds a new error onto an existing chain of errors. If the new error to
|
|
// be added is nil, then the old error is returned unchanged.
|
|
func Wrapf(err error, format string, args ...interface{}) error {
|
|
return errors.Wrapf(err, format, args...)
|
|
}
|
|
|
|
// Append can be used to safely append an error onto an existing one. If you
|
|
// pass in a nil error to append, the existing error will be returned unchanged.
|
|
// If the existing error is already nil, then the new error will be returned
|
|
// unchanged. This makes it easy to use Append as a safe `reterr += err`, when
|
|
// you don't know if either is nil or not.
|
|
func Append(reterr, err error) error {
|
|
if reterr == nil { // keep it simple, pass it through
|
|
return err // which might even be nil
|
|
}
|
|
if err == nil { // no error, so don't do anything
|
|
return reterr
|
|
}
|
|
// both are real errors
|
|
return multierror.Append(reterr, err)
|
|
}
|
|
|
|
// Join takes a list of errors and combines them into a multierror with Append.
|
|
func Join(errs []error) error {
|
|
if len(errs) == 0 {
|
|
return nil
|
|
}
|
|
if len(errs) == 1 {
|
|
return errs[0]
|
|
}
|
|
var reterr error
|
|
for _, e := range errs {
|
|
reterr = Append(reterr, e)
|
|
}
|
|
return reterr
|
|
}
|
|
|
|
// WithoutContext passes through an error if it's nil or is a normal error. If
|
|
// it is a multierror, then it removes all errors of context cancellation unless
|
|
// that's all there is. If there's one error remaining, it returns that.
|
|
// Otherwise it returns a new multierror with what's left.
|
|
func WithoutContext(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
multiErr, ok := err.(*multierror.Error)
|
|
if !ok {
|
|
return err
|
|
}
|
|
|
|
if len(multiErr.Errors) == 0 {
|
|
return err // unexpected
|
|
}
|
|
|
|
errs := []error{}
|
|
for _, e := range multiErr.Errors {
|
|
if e == context.Canceled {
|
|
continue // remove
|
|
}
|
|
errs = append(errs, e)
|
|
}
|
|
|
|
if len(errs) == 0 {
|
|
return context.Canceled
|
|
}
|
|
//if len(errs) == 1 {
|
|
// return errs[0]
|
|
//}
|
|
|
|
return Join(errs)
|
|
}
|
|
|
|
// String returns a string representation of the error. In particular, if the
|
|
// error is nil, it returns an empty string instead of panicking.
|
|
func String(err error) string {
|
|
if err == nil {
|
|
return ""
|
|
}
|
|
return err.Error()
|
|
}
|