mirror of
https://github.com/luxfi/monitoring.git
synced 2026-10-30 15:24:48 +00:00
The validator-health exporter and the rpc-tip-gate were both stdlib-only
Python scripts living in ConfigMaps on a stock python image. That choice
bought one thing — no build step — and it cost the backend standard: the
serving side of this stack is Go, and these two are serving processes.
internal/luxd — what one node says it believes (tip, finalized, peers,
bootstrapped, health-with-check-names). /v1 only.
internal/kube — the four apiserver calls fleet health makes. Not
client-go: a scheme, a codec and an informer machinery
to unmarshal four fields is not a dependency, it is a
liability.
internal/fleet — what the numbers MEAN, separated from the process that
serves them, so a metric's meaning can be tested
without a cluster.
Proven, not asserted. Both implementations were pointed at one fake
cluster and diffed:
exporter : 234 lines, 22 metric families — byte-for-byte identical.
tip-gate : same 5 decisions, same 3 merge-patches, byte-for-byte.
The differential earned its keep: the gate had been ported to
eth_getBlockByNumber when the deployed one calls eth_blockNumber. Same
answer today, a different call tomorrow. Fixed to match.
Two deliberate improvements over the Python, neither a scrape-visible
change: exposition series are sorted (Python's thread-completion order
made two scrapes of a steady fleet textually different), and the gate
refuses to start on a missing setting rather than defaulting quietly.
Co-authored-by: Hanzo Dev <[email protected]>
55 lines
1.3 KiB
Go
55 lines
1.3 KiB
Go
// rpc-tip-gate labels the luxd pods that are keeping up and unlabels the ones
|
|
// that are not, so the public RPC Service only ever selects nodes at the tip.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/luxfi/monitoring/internal/fleet"
|
|
)
|
|
|
|
func main() {
|
|
cfg := fleet.GateConfig{
|
|
Namespace: must("NAMESPACE"),
|
|
Selector: must("POD_SELECTOR"),
|
|
RPCPort: must("RPC_PORT"),
|
|
RPCPath: must("RPC_PATH"),
|
|
LabelKey: must("LABEL_KEY"),
|
|
LabelValue: must("LABEL_VALUE"),
|
|
MaxLag: uint64(mustInt("MAX_LAG_BLOCKS")),
|
|
Interval: mustSeconds("INTERVAL_SECONDS"),
|
|
Timeout: mustSeconds("PROBE_TIMEOUT_SECONDS"),
|
|
}
|
|
fleet.NewGate(cfg).Run(context.Background())
|
|
}
|
|
|
|
// must fails at startup on a missing setting. A gate that silently defaults is
|
|
// a gate that quietly stops gating.
|
|
func must(k string) string {
|
|
v := os.Getenv(k)
|
|
if v == "" {
|
|
log.Fatalf("rpc-tip-gate: %s is required", k)
|
|
}
|
|
return v
|
|
}
|
|
|
|
func mustInt(k string) int {
|
|
n, err := strconv.Atoi(must(k))
|
|
if err != nil {
|
|
log.Fatalf("rpc-tip-gate: %s: %v", k, err)
|
|
}
|
|
return n
|
|
}
|
|
|
|
func mustSeconds(k string) time.Duration {
|
|
f, err := strconv.ParseFloat(must(k), 64)
|
|
if err != nil {
|
|
log.Fatalf("rpc-tip-gate: %s: %v", k, err)
|
|
}
|
|
return time.Duration(f * float64(time.Second))
|
|
}
|