59 lines
2.8 KiB
Go
59 lines
2.8 KiB
Go
package buildstep
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Passing is a fault of the network between a build node and what it reaches (a name lookup, a timeout, a reset
|
|
// connection, a forge's sshd refusing a burst, a store that does not answer), not of the source or the toolchain. A
|
|
// build module wraps it, and the shared runner then writes the step again and settles its ask with a delayed nak, up to
|
|
// AskRuns runs (ADR 0664 §1, §2). Any other error ends the step failed at once.
|
|
type Passing struct{ Err error }
|
|
|
|
func (p *Passing) Error() string { return p.Err.Error() }
|
|
func (p *Passing) Unwrap() error { return p.Err }
|
|
|
|
// IsPassing says err is, or wraps, a Passing.
|
|
func IsPassing(err error) bool {
|
|
var p *Passing
|
|
return errors.As(err, &p)
|
|
}
|
|
|
|
// HolderSilentWords opens the words of a HolderSilent, so PassingCause reads them from a step's reason too.
|
|
const HolderSilentWords = "the holder is silent"
|
|
|
|
// HolderSilent is a seat verb that answered "no responders" while the assigner names a holder of the seat: its tool
|
|
// runner is being replaced, a moment later it answers. It is a Passing fault (ADR 0664 §1); a seat nothing holds is not.
|
|
type HolderSilent struct{ Seat, Holder, Node string }
|
|
|
|
func (h *HolderSilent) Error() string {
|
|
return fmt.Sprintf("%s: seat %s, held by %s on %s, answered no responders", HolderSilentWords, h.Seat, h.Holder, h.Node)
|
|
}
|
|
|
|
// Unwrap makes a HolderSilent a Passing.
|
|
func (h *HolderSilent) Unwrap() error { return &Passing{Err: errors.New(h.Error())} }
|
|
|
|
// passingWords are the words of a network that did not answer, lower-cased: go's and git's (a lookup, a timeout, a refused or
|
|
// reset connection, ssh's key exchange cut by a forge's per-source penalty) and npm's error codes. "no responders" alone is not
|
|
// one: it is the same for a seat nothing holds (HolderSilent is the case a holder is named).
|
|
var passingWords = []string{"i/o timeout", "no such host", "temporary failure in name resolution", "connection refused",
|
|
"connection reset", "tls handshake timeout", "network is unreachable", "no route to host", "lookup ", "kex_exchange_identification", "connection closed by",
|
|
"could not resolve host", "client.timeout", "operation timed out", "connection timed out", "eai_again", "etimedout", "econnreset", "econnrefused", "enotfound", strings.ToLower(HolderSilentWords)}
|
|
|
|
// PassingCause says words (a tool's output or a step's reason) are those of a transport fault.
|
|
func PassingCause(words string) bool {
|
|
words = strings.ToLower(words)
|
|
for _, w := range passingWords {
|
|
if strings.Contains(words, w) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// TransportFaultWords open the reason of a step that ended failed after its runs met a transport fault (the asker says them,
|
|
// "a transport fault on 3 runs (nodes): cause"): a reader tells that end from a fault of the source by them (ADR 0664 §3).
|
|
const TransportFaultWords = "a transport fault on "
|