The controller asks no build of anybody (ADR 0501 §5, ADR 0525 clause 6, ADR 0503 §3). Every module of a walk is taken from the builder's done key; a key not done yet waits, named in the plan, and never fails it. - C2b/C2c: internal/builder and cmd/mesh-controller's asks go (askABuild, onlyTheBuildersOwn, buildOneAsked, takeIn from an outcome, buildAgentsOwn, the builder's say and its fallback, the daemon hearing an outcome). The server follows the retired seats' built subjects only to let go of them, because the installer's carried user list names them. - C2d: replay, queue, cancel, clear, kill, pause, resume, delivery-check, check-here and merge-gate are refused by name. - C3: the check queues (CHECKS_READY, CHECKS_OTHER) and their workers, the checks-running and plans-building buckets and their grants, the build queue and its hold (plans_building.go, queue.go, link/holds.go, builds_nats.go, checks_nats.go). - C4: merge_gate*.go, the pull request's repo-check path, the Checked event and its layers; a pull request asks the controller nothing but to tell mesh-config what its head declares. - C5: the builds bucket is written only by key_takein. - C10 (help and refusals): the terminal's help lists only what is served. Integration tests of the plan's tiers, sends, stops and retries now run on a stand-in builder with a done key for every module (walk_keys_scaffold_integration_test.go); the tests of the asks, the queue, the merge gate and the replays are deleted.
104 lines
3.7 KiB
Go
104 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
// where a build's repository is (novox/hq ADR 0111).
|
|
//
|
|
// A repository is on the mesh's own forge, or it is anywhere else. The first is recorded as its path
|
|
// on the forge holding the git seat, and cloned from wherever that forge runs at the moment of
|
|
// building; the second is a URL, recorded and cloned exactly as given. The build machine is not told
|
|
// the difference — it is handed a URL either way — because only the control plane knows where the
|
|
// seat's holder runs.
|
|
|
|
// gitSeat is the seat a self-hosted repository lives on.
|
|
const gitSeat = "git"
|
|
|
|
// buildSource is where a build's repository is: a URL, or a path on a seat's holder.
|
|
type buildSource struct {
|
|
Repository string
|
|
Seat string
|
|
}
|
|
|
|
// String is the source as a person reads it, which for one on a seat is not the URL: the URL is a
|
|
// fact about where the forge happens to run today.
|
|
func (s buildSource) String() string {
|
|
if s.Seat == "" {
|
|
return s.Repository
|
|
}
|
|
return fmt.Sprintf("%s on the %s seat", s.Repository, s.Seat)
|
|
}
|
|
|
|
// onASeat refuses an address given as a path on the forge.
|
|
//
|
|
// **A URL here would be recorded as a path**, and then composed onto the forge's address as one —
|
|
// cloning `http://forge:3000/https://github.com/…`. Refused by what an address plainly looks like,
|
|
// not repaired: `--self` promises a path, and something that is not one is a mistake to name.
|
|
func onASeat(repository string) error {
|
|
if strings.Contains(repository, ":") || strings.HasPrefix(repository, "/") ||
|
|
strings.Trim(repository, "/") == "" {
|
|
return fmt.Errorf("--self takes the repository's path on the forge, such as novox/mesh-catalog, "+
|
|
"and %q is not one — without --self it is built from exactly what is given", repository)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// seatBase is `scheme://host:port` of a seat's holder as the mesh reaches it, for cloning.
|
|
func seatBase(world catalogue.World, seatName string) (string, error) {
|
|
seat, known := catalogue.SeatNamed(seatName)
|
|
if !known || seat.Delivers == "" {
|
|
return "", fmt.Errorf("%q is not a seat a repository can live on", seatName)
|
|
}
|
|
var holder *catalogue.Held
|
|
for i, h := range world.Held {
|
|
if h.Claim == seat.Name && h.Scope == seat.Scope {
|
|
holder = &world.Held[i]
|
|
break
|
|
}
|
|
}
|
|
if holder == nil {
|
|
return "", fmt.Errorf("nobody holds the %s seat, so nothing can be cloned from this mesh's "+
|
|
"forge — assign a module that claims it, or build from the repository's URL without --self",
|
|
seat.Name)
|
|
}
|
|
var provider *catalogue.Provider
|
|
for i, p := range world.Offered[seat.Delivers] {
|
|
if p.Node == holder.Node && p.Module == holder.Module {
|
|
provider = &world.Offered[seat.Delivers][i]
|
|
}
|
|
}
|
|
if provider == nil {
|
|
return "", fmt.Errorf("%s on %s holds the %s seat and offers no %q to clone from",
|
|
holder.Module, holder.Node, seat.Name, seat.Delivers)
|
|
}
|
|
if provider.At == "" {
|
|
return "", fmt.Errorf("%s on %s holds the %s seat and is not on the private network, so no "+
|
|
"build machine can reach it", holder.Module, holder.Node, seat.Name)
|
|
}
|
|
scheme, _ := provider.Serves["scheme"].(string)
|
|
port := servedPort(provider.Serves["port"])
|
|
if scheme == "" || port == "" {
|
|
return "", fmt.Errorf("%s on %s holds the %s seat and does not serve a scheme and a port for %q",
|
|
holder.Module, holder.Node, seat.Name, seat.Delivers)
|
|
}
|
|
return fmt.Sprintf("%s://%s:%s", scheme, provider.At, port), nil
|
|
}
|
|
|
|
// servedPort is a served port as text, however the manifest and the node's settings carried it.
|
|
func servedPort(v any) string {
|
|
switch p := v.(type) {
|
|
case float64:
|
|
return strconv.Itoa(int(p))
|
|
case int:
|
|
return strconv.Itoa(p)
|
|
case string:
|
|
return p
|
|
}
|
|
return ""
|
|
}
|