Files
mesh-delivery/sdk/buildstep/passing.go
T

70 lines
3.0 KiB
Go

package buildstep
import (
"errors"
"fmt"
"strings"
"time"
)
// 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", "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
}
// DropKey is the key of an ask's drop in a build seat's pauses state: "dropped.<id>" (the part build-step).
func DropKey(askID string) string { return "dropped." + askID }
// DroppedPrefix opens every key DropKey makes.
const DroppedPrefix = "dropped."
// Drop is the value of the key dropped.<ask id> of a build seat's pauses state: an ask its caller no longer waits for.
type Drop struct {
V int `json:"v,omitempty"`
By string `json:"by,omitempty"`
Why string `json:"why"`
At time.Time `json:"at,omitzero"`
}