Merge pull request 'Raise a machine's network from what its engine says, once (hq ADR 0241)' (#113) from feat/machine-network-health into main
This commit was merged in pull request #113.
This commit is contained in:
@@ -40,6 +40,29 @@ type NodeHealth struct {
|
||||
Resources []ResourceHealth
|
||||
// Streaks is, per module, how many statements in a row said a resource of it was unhealthy.
|
||||
Streaks map[string]int
|
||||
// Network is the machine's own networking as its engine said it (novox/hq ADR 0241); nil from an
|
||||
// engine older than that judging.
|
||||
Network *NetworkHealth
|
||||
}
|
||||
|
||||
// NetworkHealth is a machine's networking as its engine said it (ADR 0241).
|
||||
type NetworkHealth struct {
|
||||
State string `json:"state"`
|
||||
Since time.Time `json:"since"`
|
||||
Parts []NetworkPart `json:"parts"`
|
||||
}
|
||||
|
||||
// NetworkPart is one part of it: resolv-conf, names, tunnel, bus or route.
|
||||
type NetworkPart struct {
|
||||
Part string `json:"part"`
|
||||
State string `json:"state"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Said string `json:"said,omitempty"`
|
||||
Writer string `json:"writer,omitempty"`
|
||||
Owner string `json:"owner,omitempty"`
|
||||
Toward []string `json:"toward,omitempty"`
|
||||
Since time.Time `json:"since"`
|
||||
Streak int `json:"streak,omitempty"`
|
||||
}
|
||||
|
||||
// HealthOf is a machine's newest statement; false when its node-engine has never stated one.
|
||||
@@ -60,7 +83,7 @@ func (i *Inventory) Healths(ctx context.Context) (map[string]NodeHealth, error)
|
||||
|
||||
func (i *Inventory) healths(ctx context.Context, only string) (map[string]NodeHealth, error) {
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
`select n.name, h.contract, h.said_at, h.heard_at, h.resources, h.streaks
|
||||
`select n.name, h.contract, h.said_at, h.heard_at, h.resources, h.streaks, h.network
|
||||
from node_health h join node n on n.id = h.node
|
||||
where $1 = '' or n.name = $1`, only)
|
||||
if err != nil {
|
||||
@@ -70,8 +93,8 @@ func (i *Inventory) healths(ctx context.Context, only string) (map[string]NodeHe
|
||||
out := map[string]NodeHealth{}
|
||||
for rows.Next() {
|
||||
var h NodeHealth
|
||||
var resources, streaks []byte
|
||||
if err := rows.Scan(&h.Node, &h.Contract, &h.SaidAt, &h.HeardAt, &resources, &streaks); err != nil {
|
||||
var resources, streaks, network []byte
|
||||
if err := rows.Scan(&h.Node, &h.Contract, &h.SaidAt, &h.HeardAt, &resources, &streaks, &network); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(resources, &h.Resources); err != nil {
|
||||
@@ -80,6 +103,11 @@ func (i *Inventory) healths(ctx context.Context, only string) (map[string]NodeHe
|
||||
if err := json.Unmarshal(streaks, &h.Streaks); err != nil {
|
||||
return nil, fmt.Errorf("%s's health cannot be read: %w", h.Node, err)
|
||||
}
|
||||
if len(network) > 0 {
|
||||
if err := json.Unmarshal(network, &h.Network); err != nil {
|
||||
return nil, fmt.Errorf("%s's network health cannot be read: %w", h.Node, err)
|
||||
}
|
||||
}
|
||||
out[h.Node] = h
|
||||
}
|
||||
return out, rows.Err()
|
||||
@@ -102,18 +130,25 @@ func (i *Inventory) RecordHealth(ctx context.Context, h NodeHealth) (bool, error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var network []byte
|
||||
if h.Network != nil {
|
||||
if network, err = json.Marshal(h.Network); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
heard := h.HeardAt
|
||||
if heard.IsZero() {
|
||||
heard = time.Now()
|
||||
}
|
||||
var node string
|
||||
err = i.store.Pool().QueryRow(ctx,
|
||||
`insert into node_health (node, contract, said_at, heard_at, resources, streaks)
|
||||
select id, $2, $3, $4, $5, $6 from node where name = $1
|
||||
`insert into node_health (node, contract, said_at, heard_at, resources, streaks, network)
|
||||
select id, $2, $3, $4, $5, $6, $7 from node where name = $1
|
||||
on conflict (node) do update set contract = excluded.contract, said_at = excluded.said_at,
|
||||
heard_at = excluded.heard_at, resources = excluded.resources, streaks = excluded.streaks
|
||||
heard_at = excluded.heard_at, resources = excluded.resources, streaks = excluded.streaks,
|
||||
network = excluded.network
|
||||
where node_health.said_at <= excluded.said_at
|
||||
returning node`, h.Node, h.Contract, h.SaidAt, heard, resources, streaks).Scan(&node)
|
||||
returning node`, h.Node, h.Contract, h.SaidAt, heard, resources, streaks, network).Scan(&node)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
if _, nerr := i.NodeByName(ctx, h.Node); nerr != nil {
|
||||
return false, nerr
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
-- A machine says how its network is (novox/hq ADR 0241, which extends ADR 0240 from what a module runs to
|
||||
-- the machine it runs on).
|
||||
--
|
||||
-- Beside the state of every long-running resource, each machine's node-engine states its own networking:
|
||||
-- the resolver file the uplink holder declared and who rewrote it, the names through every resolver it
|
||||
-- lists, the tunnel's handshake with the hub, the bus and the default route — the worst of them, since
|
||||
-- when, and each part. Kept with the machine's newest statement, replaced with it, so `node show`, the
|
||||
-- gate and a controller started again read the same word. Null for a machine whose node-engine is older
|
||||
-- than this judging: its network is not known — never healthy, never a reason to raise anything.
|
||||
alter table node_health add column network jsonb;
|
||||
@@ -428,6 +428,45 @@ type Health struct {
|
||||
// At is when the engine looked, on the machine's clock: the order of its statements.
|
||||
At time.Time `json:"at"`
|
||||
Resources []ResourceHealth `json:"resources"`
|
||||
// Network is the machine's own networking, judged by its engine (novox/hq ADR 0241); nil from an engine
|
||||
// older than that judging, which is "not known", never healthy.
|
||||
Network *NetworkHealth `json:"network,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkHealth is a machine's networking in one statement (ADR 0241): the worst of its parts, since
|
||||
// when, and each part. The node-engine's own (mesh-host internal/link NetworkHealth).
|
||||
type NetworkHealth struct {
|
||||
State string `json:"state"`
|
||||
Since time.Time `json:"since"`
|
||||
Parts []NetworkPart `json:"parts"`
|
||||
}
|
||||
|
||||
// The parts of a machine's networking its engine judges (ADR 0241).
|
||||
const (
|
||||
PartResolvConf = "resolv-conf"
|
||||
PartNames = "names"
|
||||
PartTunnel = "tunnel"
|
||||
PartBus = "bus"
|
||||
PartRoute = "route"
|
||||
// TowardHub is what a part failing toward the hub names in Toward.
|
||||
TowardHub = "hub"
|
||||
)
|
||||
|
||||
// NetworkPart is one part, as the engine judged it on its second look.
|
||||
type NetworkPart struct {
|
||||
Part string `json:"part"`
|
||||
State string `json:"state"`
|
||||
// Reason is in words with no address, path or domain; Said is the detail, kept as evidence.
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Said string `json:"said,omitempty"`
|
||||
// Writer is the program that rewrote the resolver file, when the engine could name it; Owner the
|
||||
// module whose file it is.
|
||||
Writer string `json:"writer,omitempty"`
|
||||
Owner string `json:"owner,omitempty"`
|
||||
// Toward is what a failure points at: "hub", or each resolver's address that failed.
|
||||
Toward []string `json:"toward,omitempty"`
|
||||
Since time.Time `json:"since"`
|
||||
Streak int `json:"streak,omitempty"`
|
||||
}
|
||||
|
||||
// The states a resource is said in (ADR 0240 §4).
|
||||
|
||||
@@ -33,6 +33,13 @@ func TestTheWireFormatIsExactlyTheseFieldNames(t *testing.T) {
|
||||
[]string{"module", "resource", "kind", "target", "state", "reason", "since", "streak", "restarts",
|
||||
"check", "needs"}},
|
||||
{HealthSaid{Node: "n"}, []string{"node", "health"}},
|
||||
// novox/hq ADR 0241: the machine's own networking, beside its resources.
|
||||
{Health{Contract: ReadinessContract, Resources: []ResourceHealth{}, Network: &NetworkHealth{State: "unhealthy",
|
||||
Parts: []NetworkPart{}}}, []string{"contract", "at", "resources", "network"}},
|
||||
{NetworkHealth{State: "unhealthy", Parts: []NetworkPart{}}, []string{"state", "since", "parts"}},
|
||||
{NetworkPart{Part: "resolv-conf", State: "unhealthy", Reason: "r", Said: "s", Writer: "w", Owner: "o",
|
||||
Toward: []string{"hub"}, Streak: 2}, []string{"part", "state", "reason", "said", "writer", "owner", "toward",
|
||||
"since", "streak"}},
|
||||
} {
|
||||
raw, err := json.Marshal(c.value)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user