mesh/merge-gate head bfc64ac65ea1 merged into trunk 26de6bc62558 — pass: builds mesh-delivery-indexer, new: modules/mesh-delivery-builder → novox; no bus s…
mesh/repo-check head bfc64ac65ea1 merged into trunk 26de6bc62558 — pass: its merge-check.sh passed
mesh/delivery delivered
89 lines
4.0 KiB
Go
89 lines
4.0 KiB
Go
package build
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// ChooseNodes picks up to want build nodes for a language (ADR 0478 clause 3): the nodes whose toolchains key reports the
|
|
// version the nodes agree on, the ones with room under their build capacity first (places), then the fewest lanes the
|
|
// builder has running or chosen there, a tie broken by the node's name. A place is reserved on each node chosen, in the
|
|
// same step as the choice, so builds choosing at once do not choose the same full node while another has room; the
|
|
// caller takes the place (Places.Acquire) or gives it up (Places.Cancel). A node at its capacity is still chosen when
|
|
// the others are too (fewer than want have room): its lane waits for a place. A node qualifies only when qualifies is true
|
|
// of it (online and its build module healthy; nil means every node does). It answers the toolchain all the reporting nodes
|
|
// agree on, qualifying or not, as the indexer computes it. When nothing is reported, or the nodes report different
|
|
// versions (so the indexer agreed none), it fails naming the language and why; when no reporting node qualifies it fails
|
|
// naming the nodes. A node whose build module refuses on its toolchain refuses the step, and the build fails naming it.
|
|
func ChooseNodes(language string, entries []delivery.NodeToolchain, places *Places, qualifies func(node string) bool, want int) ([]string, delivery.Toolchain2, error) {
|
|
tc, none := delivery.AgreedToolchain(entries)
|
|
if none != "" {
|
|
return nil, delivery.Toolchain2{}, fmt.Errorf("no %s build node qualifies: %s", language, none)
|
|
}
|
|
nodes, err := choose(language, entries, places, qualifies, want)
|
|
return nodes, tc, err
|
|
}
|
|
|
|
// ChooseAnyNodes is ChooseNodes for artifacts that need no toolchain, such as an archive (ADR 0478 clause 1): the
|
|
// build module's nodes agree on nothing, so any node of the build module that qualifies is chosen, by capacity and load only.
|
|
func ChooseAnyNodes(language string, entries []delivery.NodeToolchain, places *Places, qualifies func(node string) bool, want int) ([]string, error) {
|
|
return choose(language, entries, places, qualifies, want)
|
|
}
|
|
|
|
func choose(language string, entries []delivery.NodeToolchain, places *Places, qualifies func(node string) bool, want int) ([]string, error) {
|
|
nodes := make([]string, 0, len(entries))
|
|
var reporting []string
|
|
seen := map[string]bool{}
|
|
for _, e := range entries {
|
|
if seen[e.Node] {
|
|
continue
|
|
}
|
|
seen[e.Node] = true
|
|
reporting = append(reporting, e.Node)
|
|
if qualifies == nil || qualifies(e.Node) {
|
|
nodes = append(nodes, e.Node)
|
|
}
|
|
}
|
|
if len(nodes) == 0 {
|
|
sort.Strings(reporting)
|
|
return nil, fmt.Errorf("no %s build node qualifies: %s report the toolchain, and none is online with a healthy build module", language, strings.Join(reporting, ", "))
|
|
}
|
|
if places == nil {
|
|
places = &Places{}
|
|
}
|
|
return places.choose(nodes, want), nil
|
|
}
|
|
|
|
// DigestsDifferPrefix opens the error of a build whose nodes built an artifact to different digests (ADR 0478 clause 4):
|
|
// such a key is not built again as a new revision, since the same source on the same toolchains gave two answers and
|
|
// building again settles nothing; it waits for another fingerprint.
|
|
const DigestsDifferPrefix = "digests differ:"
|
|
|
|
// Agree is the one digest every node built an artifact to (ADR 0478 clause 4), or an error naming each node and its
|
|
// digest when they differ. One node's digest agrees with itself.
|
|
func Agree(artifact string, byNode map[string]string) (string, error) {
|
|
nodes := make([]string, 0, len(byNode))
|
|
for n := range byNode {
|
|
nodes = append(nodes, n)
|
|
}
|
|
sort.Strings(nodes)
|
|
var first string
|
|
for i, n := range nodes {
|
|
if i == 0 {
|
|
first = byNode[n]
|
|
continue
|
|
}
|
|
if byNode[n] != first {
|
|
msg := fmt.Sprintf("%s the nodes built %s to different digests:", DigestsDifferPrefix, artifact)
|
|
for _, n := range nodes {
|
|
msg += fmt.Sprintf(" %s %s;", n, byNode[n])
|
|
}
|
|
return "", fmt.Errorf("%s", msg[:len(msg)-1])
|
|
}
|
|
}
|
|
return first, nil
|
|
}
|