103 lines
3.6 KiB
Go
103 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// A config value held or dropped for a pull request asks its check again (novox/hq ADR 0502 clause 5, issue 1826).
|
|
//
|
|
// The controller's composed check lays the approved values held for a pull request (mesh-config's `held`) when it
|
|
// composes, so a check run before a value was held, or after it was dropped, judged another mesh. mesh-config
|
|
// raises `config-changed` with the action hold or drop and the pull request; this owner then asks that pull
|
|
// request's check, or its group's composed check, again, the way it asks a moved head: its own path, with the
|
|
// reason in the delivery's transition, and not the `recheck` verb, which stays an exception (ADR 0421 §12).
|
|
|
|
// ConfigChangedEvent is the event this owner follows from mesh-config.
|
|
const ConfigChangedEvent = "mesh-config.config-changed"
|
|
|
|
// ConfigChange is mesh-config's `config-changed` as far as this owner reads it.
|
|
type ConfigChange struct {
|
|
Action string `json:"action"`
|
|
Pull string `json:"pull,omitempty"`
|
|
Head string `json:"head,omitempty"`
|
|
Key string `json:"key,omitempty"`
|
|
Level struct {
|
|
Node string `json:"node,omitempty"`
|
|
Module string `json:"module,omitempty"`
|
|
} `json:"level"`
|
|
}
|
|
|
|
var pullNamed = regexp.MustCompile(`^([A-Za-z0-9_.-]+)/([A-Za-z0-9_.-]+)#([0-9]+)$`)
|
|
|
|
// ConfigChanged takes a hold or a drop of a value for a pull request: every live delivery of that pull request has its
|
|
// check asked again. Anything else mesh-config says (a value set, a resolution, the merge's set) asks nothing.
|
|
func (h *Holder) ConfigChanged(c ConfigChange) {
|
|
if c.Action != "hold" && c.Action != "drop" {
|
|
return
|
|
}
|
|
m := pullNamed.FindStringSubmatch(strings.TrimSpace(c.Pull))
|
|
if m == nil {
|
|
return
|
|
}
|
|
number, _ := strconv.Atoi(m[3])
|
|
repository := strings.ToLower(m[1] + "/" + m[2])
|
|
verb := "held for"
|
|
if c.Action == "drop" {
|
|
verb = "dropped from"
|
|
}
|
|
why := fmt.Sprintf("a config value was %s this pull request: %s of %s on %s (mesh-config)", verb, c.Key,
|
|
orWord(c.Level.Module, "a module"), orWord(c.Level.Node, "a node"))
|
|
h.mu.Lock()
|
|
var ids []string
|
|
for id, d := range h.deliveries {
|
|
if d.Number == number && strings.EqualFold(d.Repository, repository) && d.MergedAs == "" && !d.State.Final() {
|
|
ids = append(ids, id)
|
|
}
|
|
}
|
|
h.mu.Unlock()
|
|
for _, id := range ids {
|
|
h.askAgainForConfig(id, why)
|
|
}
|
|
}
|
|
|
|
func orWord(s, word string) string {
|
|
if s == "" {
|
|
return word
|
|
}
|
|
return s
|
|
}
|
|
|
|
// askAgainForConfig asks one delivery's check again: a rejected or ready one is proposed again, as any judged again
|
|
// head is; a proposed one has its pending ask replaced. Its group's composed check goes out of date, and is asked
|
|
// once a member is judged again.
|
|
func (h *Holder) askAgainForConfig(id, why string) {
|
|
h.mu.Lock()
|
|
d := h.deliveries[id]
|
|
if d == nil {
|
|
h.mu.Unlock()
|
|
return
|
|
}
|
|
probe := *d
|
|
probe.Transitions = append([]Transition(nil), d.Transitions...)
|
|
_, refused := Apply(&probe, EvRecheck, true, Facts{Now: h.Now(), Why: why, By: byOwner})
|
|
proposed := d.State == Proposed
|
|
h.mu.Unlock()
|
|
switch {
|
|
case refused == nil:
|
|
if _, err := h.Recheck(id, why, byOwner); err != nil {
|
|
h.Logf("[mesh-delivery] %s: its check could not be asked again for a config value: %v", id, err)
|
|
}
|
|
case proposed:
|
|
h.askProposedAgain(id, why)
|
|
}
|
|
}
|
|
|
|
// askProposedAgain says why a proposed delivery's check is not asked again for a config value: the checker's (issue 1302), and
|
|
// whether a held config value is weighed is its composition's. Nothing is asked of the controller (issue 1318).
|
|
func (h *Holder) askProposedAgain(id, why string) {
|
|
h.Logf("[mesh-delivery] %s: a held config value changed; %v", id, checkerRefusal(id))
|
|
}
|