backend/tardigrade: Upgrade to uplink v1.1.1

This fixes issue #4370 by restoring the correct error response.
This commit is contained in:
Caleb Case 2020-06-18 16:20:56 -04:00 committed by Nick Craig-Wood
parent a55d882b7b
commit e780cda1d4
125 changed files with 14632 additions and 92919 deletions

View file

@ -1,6 +0,0 @@
language: go
go:
- 1.7
- 1.8
- tip

View file

@ -1,191 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,19 +0,0 @@
# errors [![Build Status](https://api.travis-ci.org/spacemonkeygo/errors.svg?branch=master)](https://travis-ci.org/spacemonkeygo/errors)
Please see http://godoc.org/github.com/spacemonkeygo/errors for info
### License
Copyright (C) 2014 Space Monkey, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,24 +0,0 @@
// Copyright (C) 2014 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package errors
// Config is a configuration struct meant to be used with
// github.com/spacemonkeygo/flagfile/utils.Setup
// but can be set independently.
var Config = struct {
Stacklogsize int `default:"4096" usage:"the max stack trace byte length to log"`
}{
Stacklogsize: 4096,
}

View file

@ -1,30 +0,0 @@
// Copyright (C) 2016 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build go1.7
// TODO: remove this build restriction once appengine supports 1.7
// +build !appengine
package errors
import (
"context"
)
var (
contextCanceled = context.Canceled
contextDeadlineExceeded = context.DeadlineExceeded
)

View file

@ -1,32 +0,0 @@
// Copyright (C) 2014 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package errors
import (
"sync/atomic"
)
var (
lastId int32 = 0
)
// DataKey's job is to make sure that keys in each error instances namespace
// are lexically scoped, thus helping developers not step on each others' toes
// between large packages. You can only store data on an error using a DataKey,
// and you can only make DataKeys with GenSym().
type DataKey struct{ id int32 }
// GenSym generates a brand new, never-before-seen DataKey
func GenSym() DataKey { return DataKey{id: atomic.AddInt32(&lastId, 1)} }

View file

@ -1,205 +0,0 @@
// Copyright (C) 2014 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package errors is a flexible error support library for Go
Motivation
Go's standard library is intentionally sparse on providing error utilities, and
developers coming from other programming languages may miss some features they
took for granted [1]. This package is an attempt at providing those features in
an idiomatic Go way.
The main features this package provides (in addition to miscellaneous
utilities) are:
* Error hierarchies
* Stack traces
* Arbitrary error values
Error hierarchies
While Go has very deliberately not implemented class hierarchies, a quick
perusal of Go's net and os packages should indicate that sometimes error
hierarchies are useful. Go programmers should be familiar with the net.Error
interface (and the types that fulfill it) as well as the os helper functions
such as os.IsNotExist, os.IsPermission, etc.
Unfortunately, to implement something similar, a developer will have to
implement a struct that matches the error interface as well as any testing
methods or any more detailed interfaces they may choose to export. It's not
hard, but it is friction, and developers tend to use fmt.Errorf instead due
to ease of use, thus missing out on useful features that functions like
os.IsNotExist and friends provide.
The errors package provides reusable components for building similar
features while reducing friction as much as possible. With the errors package,
the os error handling routines can be mimicked as follows:
package osmimic
import (
"github.com/spacemonkeygo/errors"
)
var (
OSError = errors.NewClass("OS Error")
NotExist = OSError.NewClass("Not Exist")
)
func Open(path string) (*File, error) {
// actually do something here
return nil, NotExist.New("path %#v doesn't exist", path)
}
func MyMethod() error {
fh, err := Open(mypath)
if err != nil {
if NotExist.Contains(err) {
// file doesn't exist, do stuff
}
return err
}
// do stuff
}
Stack traces
It doesn't take long during Go development before you may find yourself
wondering where an error came from. In other languages, as soon as an error is
raised, a stack trace is captured and is displayed as part of the language's
error handling. Go error types are simply basic values and no such magic
happens to tell you what line or what stack an error came from.
The errors package fixes this by optionally (but by default) capturing the
stack trace as part of your error. This behavior can be turned off and on for
specific error classes and comes in two flavors. You can have the stack trace
be appended to the error's Error() message, or you can have the stack trace
be logged immediately, every time an error of that type is instantiated.
Every error and error class supports hierarchical settings, in the sense that
if a setting was not explicitly set on that error or error class, setting
resolution traverses the error class hierarchy until it finds a valid setting,
or returns the default.
See CaptureStack()/NoCaptureStack() and LogOnCreation()/NoLogOnCreation() for
how to control this feature.
Arbitrary error values
These hierarchical settings (for whether or not errors captured or logged stack
traces) were so useful, we generalized the system to allow users to extend
the errors system with their own values. A user can tag a specific error with
some value given a statically defined key, or tag a whole error class subtree.
Arbitrary error values can easily handle situtations like net.Error's
Temporary() field, where some errors are temporary and others aren't. This can
be mimicked as follows:
package netmimic
import (
"github.com/spacemonkeygo/errors"
)
var (
NetError = errors.NewClass("Net Error")
OpError = NetError.NewClass("Op Error")
tempErrorKey = errors.GenSym()
)
func SetIsTemporary() errors.ErrorOption {
return errors.SetData(tempErrorKey, true)
}
func IsTemporary(err error) bool {
v, ok := errors.GetData(err, tempErrorKey).(bool)
if !ok {
return false
}
return v
}
func NetworkOp() error {
// actually do something here
return OpError.NewWith("failed operation", SetIsTemporary())
}
func Example() error {
for {
err := NetworkOp()
if err != nil {
if IsTemporary(err) {
// probably should do exponential backoff
continue
}
return err
}
}
}
HTTP handling
Another great example of arbitrary error value functionality is the errhttp
subpackage. See the errhttp source for more examples of how to use
SetData/GetData.
The errhttp package really helped clean up our error code. Take a look to
see if it can help your error handling with HTTP stacks too.
http://godoc.org/github.com/spacemonkeygo/errors/errhttp
Exit recording
So you have stack traces, which tells you how the error was generated, but
perhaps you're interested in keeping track of how the error was handled?
Every time you call errors.Record(err), it adds the current line information
to the error's output. As an example:
func MyFunction() error {
err := Something()
if err != nil {
if IsTemporary(err) {
// manage the temporary error
return errors.Record(err)
} else {
// manage the permanent error
return errors.Record(err)
}
}
}
errors.Record will help you keep track of which error handling branch your
code took.
ErrorGroup
There's a few different types of ErrorGroup utilities in this package, but they
all work the same way. Make sure to check out the ErrorGroup example.
CatchPanic
CatchPanic helps you easily manage functions that you think might panic, and
instead return errors. CatchPanic works by taking a pointer to your named error
return value. Check out the CatchPanic example for more.
Footnotes
[1] This errors package started while porting a large Python codebase to Go.
https://www.spacemonkey.com/blog/posts/go-space-monkey
*/
package errors

View file

@ -1,648 +0,0 @@
// Copyright (C) 2014 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package errors
import (
"errors"
"fmt"
"io"
"net"
"os"
"path/filepath"
"runtime"
"strings"
)
var (
logOnCreation = GenSym()
captureStack = GenSym()
disableInheritance = GenSym()
)
// ErrorClass is the basic hierarchical error type. An ErrorClass generates
// actual errors, but the error class controls properties of the errors it
// generates, such as where those errors are in the hierarchy, whether or not
// they capture the stack on instantiation, and so forth.
type ErrorClass struct {
parent *ErrorClass
name string
data map[DataKey]interface{}
}
var (
// HierarchicalError is the base class for all hierarchical errors generated
// through this class.
HierarchicalError = &ErrorClass{
parent: nil,
name: "Error",
data: map[DataKey]interface{}{captureStack: true}}
// SystemError is the base error class for errors not generated through this
// errors library. It is not expected that anyone would ever generate new
// errors from a SystemError type or make subclasses.
SystemError = &ErrorClass{
parent: nil,
name: "System Error",
data: map[DataKey]interface{}{}}
)
// An ErrorOption is something that controls behavior of specific error
// instances. They can be set on ErrorClasses or errors individually.
type ErrorOption func(map[DataKey]interface{})
// SetData will take the given value and store it with the error or error class
// and its descendents associated with the given DataKey. Be sure to check out
// the example. value can be nil to disable values for subhierarchies.
func SetData(key DataKey, value interface{}) ErrorOption {
return func(m map[DataKey]interface{}) {
m[key] = value
}
}
// LogOnCreation tells the error class and its descendents to log the stack
// whenever an error of this class is created.
func LogOnCreation() ErrorOption {
return SetData(logOnCreation, true)
}
// CaptureStack tells the error class and its descendents to capture the stack
// whenever an error of this class is created, and output it as part of the
// error's Error() method. This is the default.
func CaptureStack() ErrorOption {
return SetData(captureStack, true)
}
// NoLogOnCreation is the opposite of LogOnCreation and applies to the error,
// class, and its descendents. This is the default.
func NoLogOnCreation() ErrorOption {
return SetData(logOnCreation, false)
}
// NoCaptureStack is the opposite of CaptureStack and applies to the error,
// class, and its descendents.
func NoCaptureStack() ErrorOption {
return SetData(captureStack, false)
}
// If DisableInheritance is provided, the error or error class will belong to
// its ancestors, but will not inherit their settings and options. Use with
// caution, and may disappear in future releases.
func DisableInheritance() ErrorOption {
return SetData(disableInheritance, true)
}
func boolWrapper(val interface{}, default_value bool) bool {
rv, ok := val.(bool)
if ok {
return rv
}
return default_value
}
// NewClass creates an error class with the provided name and options. Classes
// generated from this method and not *ErrorClass.NewClass will descend from
// the root HierarchicalError base class.
func NewClass(name string, options ...ErrorOption) *ErrorClass {
return HierarchicalError.NewClass(name, options...)
}
// New is for compatibility with the default Go errors package. It simply
// creates an error from the HierarchicalError root class.
func New(text string) error {
// NewWith doesn't take a format string, even though we have no options.
return HierarchicalError.NewWith(text)
}
// NewClass creates an error class with the provided name and options. The new
// class will descend from the receiver.
func (parent *ErrorClass) NewClass(name string,
options ...ErrorOption) *ErrorClass {
ec := &ErrorClass{
parent: parent,
name: name,
data: make(map[DataKey]interface{})}
for _, option := range options {
option(ec.data)
}
if !boolWrapper(ec.data[disableInheritance], false) {
// hoist options for speed
for key, val := range parent.data {
_, exists := ec.data[key]
if !exists {
ec.data[key] = val
}
}
return ec
} else {
delete(ec.data, disableInheritance)
}
return ec
}
// MustAddData allows adding data key value pairs to error classes after they
// are created. This is useful for allowing external packages add namespaced
// values to errors defined outside of their package. It will panic if the
// key is already set in the error class.
func (e *ErrorClass) MustAddData(key DataKey, value interface{}) {
if _, ex := e.data[key]; ex {
panic("key already exists")
}
e.data[key] = value
}
// GetData will return any data set on the error class for the given key. It
// returns nil if there is no data set for that key.
func (e *ErrorClass) GetData(key DataKey) interface{} {
return e.data[key]
}
// Parent returns this error class' direct ancestor.
func (e *ErrorClass) Parent() *ErrorClass {
return e.parent
}
// String returns this error class' name
func (e *ErrorClass) String() string {
if e == nil {
return "nil"
}
return e.name
}
// Is returns true if the receiver class is or is a descendent of parent.
func (e *ErrorClass) Is(parent *ErrorClass) bool {
for check := e; check != nil; check = check.parent {
if check == parent {
return true
}
}
return false
}
// frame logs the pc at some point during execution.
type frame struct {
pc uintptr
}
// String returns a human readable form of the frame.
func (e frame) String() string {
if e.pc == 0 {
return "unknown.unknown:0"
}
f := runtime.FuncForPC(e.pc)
if f == nil {
return "unknown.unknown:0"
}
file, line := f.FileLine(e.pc)
return fmt.Sprintf("%s:%s:%d", f.Name(), filepath.Base(file), line)
}
// callerState records the pc into an frame for two callers up.
func callerState(depth int) frame {
pc, _, _, ok := runtime.Caller(depth)
if !ok {
return frame{pc: 0}
}
return frame{pc: pc}
}
// record will record the pc at the given depth into the error if it is
// capable of recording it.
func record(err error, depth int) error {
if err == nil {
return nil
}
cast, ok := err.(*Error)
if !ok {
return err
}
cast.exits = append(cast.exits, callerState(depth))
return cast
}
// Record will record the current pc on the given error if possible, adding
// to the error's recorded exits list. Returns the given error argument.
func Record(err error) error {
return record(err, 3)
}
// RecordBefore will record the pc depth frames above the current stack frame
// on the given error if possible, adding to the error's recorded exits list.
// Record(err) is equivalent to RecordBefore(err, 0). Returns the given error
// argument.
func RecordBefore(err error, depth int) error {
return record(err, 3+depth)
}
// Error is the type that represents a specific error instance. It is not
// expected that you will work with *Error classes directly. Instead, you
// should use the 'error' interface and errors package methods that operate
// on errors instances.
type Error struct {
err error
class *ErrorClass
stacks [][]frame
exits []frame
data map[DataKey]interface{}
}
// GetData returns the value associated with the given DataKey on this error
// or any of its ancestors. Please see the example for SetData
func (e *Error) GetData(key DataKey) interface{} {
if e.data != nil {
val, ok := e.data[key]
if ok {
return val
}
if boolWrapper(e.data[disableInheritance], false) {
return nil
}
}
return e.class.data[key]
}
// GetData returns the value associated with the given DataKey on this error
// or any of its ancestors. Please see the example for SetData
func GetData(err error, key DataKey) interface{} {
cast, ok := err.(*Error)
if ok {
return cast.GetData(key)
}
return nil
}
func (e *ErrorClass) wrap(err error, classes []*ErrorClass,
options []ErrorOption) error {
if err == nil {
return nil
}
if ec, ok := err.(*Error); ok {
if ec.Is(e) {
if len(options) == 0 {
return ec
}
// if we have options, we have to wrap it cause we don't want to
// mutate the existing error.
} else {
for _, class := range classes {
if ec.Is(class) {
return err
}
}
}
}
rv := &Error{err: err, class: e}
if len(options) > 0 {
rv.data = make(map[DataKey]interface{})
for _, option := range options {
option(rv.data)
}
}
if boolWrapper(rv.GetData(captureStack), false) {
rv.stacks = [][]frame{getStack(3)}
}
if boolWrapper(rv.GetData(logOnCreation), false) {
LogWithStack(rv.Error())
}
return rv
}
func getStack(depth int) (stack []frame) {
var pcs [256]uintptr
amount := runtime.Callers(depth+1, pcs[:])
stack = make([]frame, amount)
for i := 0; i < amount; i++ {
stack[i] = frame{pcs[i]}
}
return stack
}
// AttachStack adds another stack to the current error's stack trace if it
// exists
func AttachStack(err error) {
if err == nil {
return
}
cast, ok := err.(*Error)
if !ok {
return
}
if len(cast.stacks) < 1 {
// only record stacks if this error was supposed to
return
}
cast.stacks = append(cast.stacks, getStack(2))
}
// WrapUnless wraps the given error in the receiver error class unless the
// error is already an instance of one of the provided error classes.
func (e *ErrorClass) WrapUnless(err error, classes ...*ErrorClass) error {
return e.wrap(err, classes, nil)
}
// Wrap wraps the given error in the receiver error class with the provided
// error-specific options.
func (e *ErrorClass) Wrap(err error, options ...ErrorOption) error {
return e.wrap(err, nil, options)
}
// New makes a new error type. It takes a format string.
func (e *ErrorClass) New(format string, args ...interface{}) error {
return e.wrap(fmt.Errorf(format, args...), nil, nil)
}
// NewWith makes a new error type with the provided error-specific options.
func (e *ErrorClass) NewWith(message string, options ...ErrorOption) error {
return e.wrap(errors.New(message), nil, options)
}
// Error conforms to the error interface. Error will return the backtrace if
// it was captured and any recorded exits.
func (e *Error) Error() string {
message := strings.TrimRight(e.err.Error(), "\n ")
if strings.Contains(message, "\n") {
message = fmt.Sprintf("%s:\n %s", e.class.String(),
strings.Replace(message, "\n", "\n ", -1))
} else {
message = fmt.Sprintf("%s: %s", e.class.String(), message)
}
if stack := e.Stack(); stack != "" {
message = fmt.Sprintf(
"%s\n\"%s\" backtrace:\n%s", message, e.class, stack)
}
if exits := e.Exits(); exits != "" {
message = fmt.Sprintf(
"%s\n\"%s\" exits:\n%s", message, e.class, exits)
}
return message
}
// Message returns just the error message without the backtrace or exits.
func (e *Error) Message() string {
message := strings.TrimRight(GetMessage(e.err), "\n ")
if strings.Contains(message, "\n") {
return fmt.Sprintf("%s:\n %s", e.class.String(),
strings.Replace(message, "\n", "\n ", -1))
}
return fmt.Sprintf("%s: %s", e.class.String(), message)
}
// WrappedErr returns the wrapped error, if the current error is simply
// wrapping some previously returned error or system error. You probably want
// the package-level WrappedErr
func (e *Error) WrappedErr() error {
return e.err
}
// WrappedErr returns the wrapped error, if the current error is simply
// wrapping some previously returned error or system error. If the error isn't
// hierarchical it is just returned.
func WrappedErr(err error) error {
cast, ok := err.(*Error)
if !ok {
return err
}
return cast.WrappedErr()
}
// Class will return the appropriate error class for the given error. You
// probably want the package-level GetClass.
func (e *Error) Class() *ErrorClass {
return e.class
}
// Name returns the name of the error: in this case the name of the class the
// error belongs to.
func (e *Error) Name() (string, bool) {
return e.class.name, true
}
// GetClass will return the appropriate error class for the given error.
// If the error is not nil, GetClass always returns a hierarchical error class,
// and even attempts to determine a class for common system error types.
func GetClass(err error) *ErrorClass {
if err == nil {
return nil
}
cast, ok := err.(*Error)
if !ok {
return findSystemErrorClass(err)
}
return cast.class
}
// Stack will return the stack associated with the error if one is found. You
// probably want the package-level GetStack.
func (e *Error) Stack() string {
if len(e.stacks) > 0 {
var frames []string
for _, stack := range e.stacks {
if frames == nil {
frames = make([]string, 0, len(stack))
} else {
frames = append(frames, "----- attached stack -----")
}
for _, f := range stack {
frames = append(frames, f.String())
}
}
return strings.Join(frames, "\n")
}
return ""
}
// GetStack will return the stack associated with the error if one is found.
func GetStack(err error) string {
if err == nil {
return ""
}
cast, ok := err.(*Error)
if !ok {
return ""
}
return cast.Stack()
}
// Exits will return the exits recorded on the error if any are found. You
// probably want the package-level GetExits.
func (e *Error) Exits() string {
if len(e.exits) > 0 {
exits := make([]string, len(e.exits))
for i, ex := range e.exits {
exits[i] = ex.String()
}
return strings.Join(exits, "\n")
}
return ""
}
// GetExits will return the exits recorded on the error if any are found.
func GetExits(err error) string {
if err == nil {
return ""
}
cast, ok := err.(*Error)
if !ok {
return ""
}
return cast.Exits()
}
// GetMessage returns just the error message without the backtrace or exits.
func GetMessage(err error) string {
if err == nil {
return ""
}
cast, ok := err.(*Error)
if !ok {
return err.Error()
}
return cast.Message()
}
// EquivalenceOption values control behavior of determining whether or not an
// error belongs to a specific class.
type EquivalenceOption int
const (
// If IncludeWrapped is used, wrapped errors are also used for determining
// class membership.
IncludeWrapped EquivalenceOption = 1
)
func combineEquivOpts(opts []EquivalenceOption) (rv EquivalenceOption) {
for _, opt := range opts {
rv |= opt
}
return rv
}
// Is returns whether or not an error belongs to a specific class. Typically
// you should use Contains instead.
func (e *Error) Is(ec *ErrorClass, opts ...EquivalenceOption) bool {
return ec.Contains(e, opts...)
}
// Contains returns whether or not the receiver error class contains the given
// error instance.
func (e *ErrorClass) Contains(err error, opts ...EquivalenceOption) bool {
if err == nil {
return false
}
cast, ok := err.(*Error)
if !ok {
return findSystemErrorClass(err).Is(e)
}
if cast.class.Is(e) {
return true
}
if combineEquivOpts(opts)&IncludeWrapped == 0 {
return false
}
return e.Contains(cast.err, opts...)
}
var (
// Useful error classes
NotImplementedError = NewClass("Not Implemented Error", LogOnCreation())
ProgrammerError = NewClass("Programmer Error", LogOnCreation())
PanicError = NewClass("Panic Error", LogOnCreation())
// The following SystemError descendants are provided such that the GetClass
// method has something to return for standard library error types not
// defined through this class.
//
// It is not expected that anyone would create instances of these classes.
//
// from os
SyscallError = SystemError.NewClass("Syscall Error")
// from syscall
ErrnoError = SystemError.NewClass("Errno Error")
// from net
NetworkError = SystemError.NewClass("Network Error")
UnknownNetworkError = NetworkError.NewClass("Unknown Network Error")
AddrError = NetworkError.NewClass("Addr Error")
InvalidAddrError = AddrError.NewClass("Invalid Addr Error")
NetOpError = NetworkError.NewClass("Network Op Error")
NetParseError = NetworkError.NewClass("Network Parse Error")
DNSError = NetworkError.NewClass("DNS Error")
DNSConfigError = DNSError.NewClass("DNS Config Error")
// from io
IOError = SystemError.NewClass("IO Error")
EOF = IOError.NewClass("EOF")
ClosedPipeError = IOError.NewClass("Closed Pipe Error")
NoProgressError = IOError.NewClass("No Progress Error")
ShortBufferError = IOError.NewClass("Short Buffer Error")
ShortWriteError = IOError.NewClass("Short Write Error")
UnexpectedEOFError = IOError.NewClass("Unexpected EOF Error")
// from context
ContextError = SystemError.NewClass("Context Error")
ContextCanceled = ContextError.NewClass("Canceled")
ContextTimeout = ContextError.NewClass("Timeout")
)
func findSystemErrorClass(err error) *ErrorClass {
switch err {
case io.EOF:
return EOF
case io.ErrUnexpectedEOF:
return UnexpectedEOFError
case io.ErrClosedPipe:
return ClosedPipeError
case io.ErrNoProgress:
return NoProgressError
case io.ErrShortBuffer:
return ShortBufferError
case io.ErrShortWrite:
return ShortWriteError
case contextCanceled:
return ContextCanceled
case contextDeadlineExceeded:
return ContextTimeout
default:
break
}
if isErrnoError(err) {
return ErrnoError
}
switch err.(type) {
case *os.SyscallError:
return SyscallError
case net.UnknownNetworkError:
return UnknownNetworkError
case *net.AddrError:
return AddrError
case net.InvalidAddrError:
return InvalidAddrError
case *net.OpError:
return NetOpError
case *net.ParseError:
return NetParseError
case *net.DNSError:
return DNSError
case *net.DNSConfigError:
return DNSConfigError
case net.Error:
return NetworkError
default:
return SystemError
}
}

View file

@ -1,26 +0,0 @@
// Copyright (C) 2014 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !appengine
package errors
import (
"syscall"
)
func isErrnoError(err error) bool {
_, ok := err.(syscall.Errno)
return ok
}

View file

@ -1,21 +0,0 @@
// Copyright (C) 2014 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build appengine
package errors
func isErrnoError(err error) bool {
return false
}

View file

@ -1,155 +0,0 @@
// Copyright (C) 2014 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package errors
import (
"fmt"
"log"
"runtime"
"strings"
)
var (
// Change this method if you want errors to log somehow else
LogMethod = log.Printf
ErrorGroupError = NewClass("Error Group Error")
)
// LogWithStack will log the given messages with the current stack
func LogWithStack(messages ...interface{}) {
buf := make([]byte, Config.Stacklogsize)
buf = buf[:runtime.Stack(buf, false)]
LogMethod("%s\n%s", fmt.Sprintln(messages...), buf)
}
// CatchPanic can be used to catch panics and turn them into errors. See the
// example.
func CatchPanic(err_ref *error) {
r := recover()
if r == nil {
return
}
err, ok := r.(error)
if ok {
*err_ref = PanicError.Wrap(err)
return
}
*err_ref = PanicError.New("%v", r)
}
// ErrorGroup is a type for collecting errors from a bunch of independent
// tasks. ErrorGroups are not threadsafe. See the example for usage.
type ErrorGroup struct {
Errors []error
limit int
excess int
}
// NewErrorGroup makes a new ErrorGroup
func NewErrorGroup() *ErrorGroup { return &ErrorGroup{} }
// NewBoundedErrorGroup makes a new ErrorGroup that will not track more than
// limit errors. Once the limit is reached, the ErrorGroup will track
// additional errors as excess.
func NewBoundedErrorGroup(limit int) *ErrorGroup {
return &ErrorGroup{
limit: limit,
}
}
// Add is called with errors. nil errors are ignored.
func (e *ErrorGroup) Add(err error) {
if err == nil {
return
}
if e.limit > 0 && len(e.Errors) == e.limit {
e.excess++
} else {
e.Errors = append(e.Errors, err)
}
}
// Finalize will collate all the found errors. If no errors were found, it will
// return nil. If one error was found, it will be returned directly. Otherwise
// an ErrorGroupError will be returned.
func (e *ErrorGroup) Finalize() error {
if len(e.Errors) == 0 {
return nil
}
if len(e.Errors) == 1 && e.excess == 0 {
return e.Errors[0]
}
msgs := make([]string, 0, len(e.Errors))
for _, err := range e.Errors {
msgs = append(msgs, err.Error())
}
if e.excess > 0 {
msgs = append(msgs, fmt.Sprintf("... and %d more.", e.excess))
e.excess = 0
}
e.Errors = nil
return ErrorGroupError.New(strings.Join(msgs, "\n"))
}
// LoggingErrorGroup is similar to ErrorGroup except that instead of collecting
// all of the errors, it logs the errors immediately and just counts how many
// non-nil errors have been seen. See the ErrorGroup example for usage.
type LoggingErrorGroup struct {
name string
total int
failed int
}
// NewLoggingErrorGroup returns a new LoggingErrorGroup with the given name.
func NewLoggingErrorGroup(name string) *LoggingErrorGroup {
return &LoggingErrorGroup{name: name}
}
// Add will handle a given error. If the error is non-nil, total and failed
// are both incremented and the error is logged. If the error is nil, only
// total is incremented.
func (e *LoggingErrorGroup) Add(err error) {
e.total++
if err != nil {
LogMethod("%s: %s", e.name, err)
e.failed++
}
}
// Finalize returns no error if no failures were observed, otherwise it will
// return an ErrorGroupError with statistics about the observed errors.
func (e *LoggingErrorGroup) Finalize() (err error) {
if e.failed > 0 {
err = ErrorGroupError.New("%s: %d of %d failed.", e.name, e.failed,
e.total)
}
e.total = 0
e.failed = 0
return err
}
type Finalizer interface {
Finalize() error
}
// Finalize takes a group of ErrorGroups and joins them together into one error
func Finalize(finalizers ...Finalizer) error {
var errs ErrorGroup
for _, finalizer := range finalizers {
errs.Add(finalizer.Finalize())
}
return errs.Finalize()
}

View file

@ -1,26 +0,0 @@
// Copyright (C) 2016 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !go1.7
package errors
import (
"golang.org/x/net/context"
)
var (
contextCanceled = context.Canceled
contextDeadlineExceeded = context.DeadlineExceeded
)

View file

@ -23,6 +23,7 @@
package infectious
import (
"errors"
"sort"
)
@ -50,7 +51,7 @@ func (f *FEC) Decode(dst []byte, shares []Share) ([]byte, error) {
}
if len(shares) == 0 {
return nil, Error.New("must specify at least one share")
return nil, errors.New("must specify at least one share")
}
piece_len := len(shares[0].Data)
result_len := piece_len * f.k
@ -78,7 +79,7 @@ func (f *FEC) decode(shares []Share, output func(Share)) error {
// mutating the underlying byte slices and reordering the shares
func (fc *FEC) Correct(shares []Share) error {
if len(shares) < fc.k {
return Error.New("must specify at least the number of required shares")
return errors.New("must specify at least the number of required shares")
}
sort.Sort(byNumber(shares))
@ -124,7 +125,7 @@ func (fc *FEC) berlekampWelch(shares []Share, index int) ([]byte, error) {
q := e + k // def of Q polynomial
if e <= 0 {
return nil, NotEnoughShares.New("")
return nil, NotEnoughShares
}
const interp_base = gfVal(2)
@ -194,7 +195,7 @@ func (fc *FEC) berlekampWelch(shares []Share, index int) ([]byte, error) {
}
if !rem.isZero() {
return nil, TooManyErrors.New("")
return nil, TooManyErrors
}
out := make([]byte, fc.n)

View file

@ -36,13 +36,15 @@
package infectious
import (
"github.com/spacemonkeygo/errors"
"errors"
"golang.org/x/sys/cpu"
)
var Error = errors.NewClass("infectious")
var NotEnoughShares = Error.NewClass("not enough shares")
var TooManyErrors = Error.NewClass("too many errors")
var hasAVX2 = cpu.X86.HasAVX2
var hasSSSE3 = cpu.X86.HasSSSE3
var (
NotEnoughShares = errors.New("not enough shares")
TooManyErrors = errors.New("too many errors to reconstruct")
)

View file

@ -33,7 +33,11 @@
package infectious
import "sort"
import (
"errors"
"fmt"
"sort"
)
// FEC represents operations performed on a Reed-Solomon-based
// forward error correction code. Make sure to construct using NewFEC.
@ -50,7 +54,7 @@ type FEC struct {
// exist, corrupted data can be detected and recovered from.
func NewFEC(k, n int) (*FEC, error) {
if k <= 0 || n <= 0 || k > 256 || n > 256 || k > n {
return nil, Error.New("requires 1 <= k <= n <= 256")
return nil, errors.New("requires 1 <= k <= n <= 256")
}
enc_matrix := make([]byte, n*k)
@ -127,7 +131,7 @@ func (f *FEC) Encode(input []byte, output func(Share)) error {
enc_matrix := f.enc_matrix
if size%k != 0 {
return Error.New("input length must be a multiple of %d", k)
return fmt.Errorf("input length must be a multiple of %d", k)
}
block_size := size / k
@ -173,21 +177,21 @@ func (f *FEC) EncodeSingle(input, output []byte, num int) error {
enc_matrix := f.enc_matrix
if num < 0 {
return Error.New("num must be non-negative")
return errors.New("num must be non-negative")
}
if num >= n {
return Error.New("num must be less than %d", n)
return fmt.Errorf("num must be less than %d", n)
}
if size%k != 0 {
return Error.New("input length must be a multiple of %d", k)
return fmt.Errorf("input length must be a multiple of %d", k)
}
block_size := size / k
if len(output) != block_size {
return Error.New("output length must be %d", block_size)
return fmt.Errorf("output length must be %d", block_size)
}
if num < k {
@ -246,7 +250,7 @@ func (f *FEC) Rebuild(shares []Share, output func(Share)) error {
enc_matrix := f.enc_matrix
if len(shares) < k {
return NotEnoughShares.New("")
return NotEnoughShares
}
share_size := len(shares[0].Data)
@ -275,7 +279,7 @@ func (f *FEC) Rebuild(shares []Share, output func(Share)) error {
}
if share_id >= n {
return Error.New("invalid share id: %d", share_id)
return fmt.Errorf("invalid share id: %d", share_id)
}
if share_id < k {

View file

@ -23,6 +23,7 @@
package infectious
import (
"errors"
"fmt"
"strings"
"unsafe"
@ -53,7 +54,7 @@ func (a gfVal) mul(b gfVal) gfVal {
func (a gfVal) div(b gfVal) (gfVal, error) {
if b == 0 {
return 0, Error.New("divide by zero")
return 0, errors.New("divide by zero")
}
if a == 0 {
return 0, nil
@ -71,7 +72,7 @@ func (a gfVal) isZero() bool {
func (a gfVal) inv() (gfVal, error) {
if a == 0 {
return 0, Error.New("invert zero")
return 0, errors.New("invert zero")
}
return gfVal(gf_exp[255-gf_log[a]]), nil
}
@ -173,7 +174,7 @@ func (p gfPoly) div(b gfPoly) (q, r gfPoly, err error) {
b = b[1:]
}
if len(b) == 0 {
return nil, nil, Error.New("divide by zero")
return nil, nil, errors.New("divide by zero")
}
// sanitize the base poly as well
@ -221,7 +222,7 @@ func (p gfPoly) div(b gfPoly) (q, r gfPoly, err error) {
p = p.add(padded)
if !p[0].isZero() {
return nil, nil, Error.New("alg error: %x", p)
return nil, nil, fmt.Errorf("alg error: %x", p)
}
p = p[1:]
}

5
vendor/github.com/vivint/infectious/go.mod generated vendored Normal file
View file

@ -0,0 +1,5 @@
module github.com/vivint/infectious
go 1.12
require golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd

2
vendor/github.com/vivint/infectious/go.sum generated vendored Normal file
View file

@ -0,0 +1,2 @@
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View file

@ -33,7 +33,10 @@
package infectious
import "bytes"
import (
"bytes"
"errors"
)
type pivotSearcher struct {
k int
@ -66,7 +69,7 @@ func (p *pivotSearcher) search(col int, matrix []byte) (int, int, error) {
}
}
return 0, 0, Error.New("pivot not found")
return 0, 0, errors.New("pivot not found")
}
func swap(a, b *byte) {
@ -100,7 +103,7 @@ func invertMatrix(matrix []byte, k int) error {
c := pivot_row[icol]
if c == 0 {
return Error.New("singular matrix")
return errors.New("singular matrix")
}
if c != 1 {